1、正则库的简单使用
bool std::regex_match (const std::string &src, std::smatches, std::regex &e)
src:原始字符串
matches:正则表达式可以从原始字符串中匹配并提取符合某种规则的数据,提取的数据就放在matches中,是一个类似于数组的容器
e:正则表达式的匹配规则
返回值:用于确定匹配是否成功
正则表达式的简单使用:
#include <iostream>
#include <string>
#include <regex>
int main()
{
std::string str = "/numbers/1234";//提取字符串中的数字字符串
//---匹配以/numbers/起始,后面跟一个或多个数字字符的字符串,并且在匹配的过程中提取这个匹配到的数字字符串
std::regex e("/numbers/(\\d+)");//匹配规则
// /numbers/ 是一个普通字符串字面量,表示要匹配的字符串中必须包含这个字串,且字符顺序号大小都要完全一致
// (\\d+) 是一个捕获组 \d 表示匹配一个数字字符(0-9)
// + 是一个量词,表示前面的元素(这里是 \d,即数字字符)要出现一次或多次
//在 \d 前面的反斜杠 \ 需要转义,因为在 C++ 字符串字面量中,反斜杠本身也有转义字符的作用,所以要写成 \\ 才能表示正则表达式中的一个反斜杠。
//---作用---
//匹配形如 /numbers/ 后面跟着一个或多个数字的字符串,
//并且将后面的数字部分作为一个捕获组提取出来,方便后续对其进行处理。
std::smatch matches;
bool ret = std::regex_match(str, matches, e);
if(ret == false)
{
return -1;
}
//肯定首先存储到的是原始字符串 然后再存数组字符串
for(auto &s :matches)
{
std::cout << s << std::endl;
}
return 0;
}
HTTP请求行中请求方法的匹配:
#include <iostream>
#include <string>
#include <regex>
int main()
{
//HTTP请求行格式: 请求方法 GET /baidu/login?user=xiaoming&pass=123123 HTTP/1.1\r\n
std::string str = "GET /baidu/login?user=xiaoming&pass=123123 HTTP/1.1\r\n";
std::smatch matches;
//1、请求方法的匹配 GET HEAD POST PUT DELETE
//2、资源路径的提取 GET之后空格 到?之前结束
//3、查询字符串提取 ?之后空格之前的
//4、HTTP协议版本提取
std::regex e("(GET|HEAD|POST|PUT|DELETE) ([^?]*)(?:\\?(.*))? (HTTP/1\\.[01])(?:\n|\r\n)?");
//注意: (空格)表示匹配空格,上一个正则表达式会匹配到下一个正则表达式的内容之前停止匹配
//括号表示要提取的数据
//[^?] 表示匹配非?字符
//\\?表示原始? (.*) 表示提取?之后的任意字符0次或多次 ,直到遇到空格
//(HTTP/1\\.[01]) 表示以HTTP/1.开始 \\. 表示原始的. 字符 [01]表示匹配0或1 其中的一个
//.是匹配任意非\n\r的字符 *匹配0次或多次
//(?:\n|\r\n)? (?: ...) 表示匹配某个格式字符串 但是不提取 最后的? 表示匹配前边的表达式0次或1次
//如果请求格式里面没有 查询字符串将 \\?(.*)写为(?:\\?(.*))? ---即可以有 也可以没有
bool ret = std::regex_match(str, matches, e);
if(ret = false)
{
return -1;
}
for(auto &s : matches)
{
std::cout << s << std::endl;
}
return 0;
}
2、通用类型any类型的实现
我们的这个高并发服务器是可以支持各种不同的协议,我们进行协议支持的时候,socket有数据到来了,我们去接收数据的时候,缓冲区中的数据不足一条完整的请求,或者比一条完整的请求多。我们就得考虑将socket缓冲区中的数据进行处理,那么数据处理到一半的时候,就得考虑在下次数据到来的时候继续处理,因此,我们就需要给服务器中的每一个连接都设置一个协议处理的上下文,专门用来控制请求数据接收和处理的节奏的。(接收到的数据和解析)
我们的服务器不单单要支持http协议,还要支持其他不同的协议,所以就不能是一个固定格式的上下文,因此我们就要有容器去接收不同结构的数据
1、一个连接必须拥有一个请求接收与解析的上下文
2、上下文的类型不能固定,因为服务器支持的协议可能会不断增多,不同的协议,可能会有不同的上下文结构
结论:必须拥有一个容器,能够保存各种不同的类型结构数据
设计实现一个any类:
1、是一个容器,容器中可以保存不同类型的数据
解决方案:
a、模板:
template<class T>
class Any{private:
T _content;
}
实例化对象的时候,必须指定容器保存的数据类型: Any <int> a;
而我们需要的是: Any a; a = 10; a = "abc..." 可以接收不同类型的数据
因此,模板是搞不定的。
b、嵌套一下,设计一个类,专门用于保存其他类型的数据,而Any类保存的是固定类的对象
class Any //Any类实现一个通用的类型容器,可以存储任意类型的值 { private: class holder//私有内部类 为存储不同类型值的具体实现提供一个公共抽象接口或基类结构 { //.... }; template <class T>//可以根据不同的类型T实例化不同的 placeholer 类 class placeholer : public holder//表示 placeholer 类是从 holder 类派生而来的 //这是实现多态的一种方式,使得 placeholer 类可以作为 holder 类的一种具体实现 { T _val; }; holder *_content;//声明了一个 Any 类的私有成员变量 _content //它是一个指向 holder 类的指针 //这个指针的作用是用于存储实际的类型值的对象 //通过这个指针,Any 类可以在运行时根据存储的值的实际类型来操作和管理这些值, //实现了存储任意类型值的功能 //holder指针_content可以指向不同类型的placeholer对象,这为实现多态奠定了基础 //多态允许在运行时根据对象的实际类型调用相应的方法 };
placeholer类继承自holder类,通过继承可以实现多态,让Any类可以使用holder指针来管理不同类型的placeholer对象。
Any类中,保存的是holder类的指针,当Any容器需要保存一个数据的时候,只需要通过placeholder子类实例化一个特定类型的子类对象出来,让子类对象保存数据。
简单代码实现:
#include <iostream>
#include <typeinfo>
#include <cassert>
class Any
{
private:
class holder //基类
{
public:
virtual ~holder();
//设置为纯虚函数,是为了让派生类实现该函数,从而实现多态
virtual const std::type_info& type() = 0;
virtual holder *clone() = 0;
};
template<class T>
class placeholder: public holder //派生类
{
public:
placeholder(const T &val) :_val(val){}
virtual const std::type_info& type() //获取子类对象保存的数据类型
{
return typeid(T);//通过 typeid(T) 能获取 T 的类型信息,并且返回其常量引用,这样就能在运行时得知 Any 容器里存储的数据类型
}
virtual holder *clone()//针对当前的对象自身克隆出一个新的子类对象
{
return new placeholder(_val); //创建新的placeholder对象,这个对象存储数据和当前对象相同 最后返回指向新对象的holder指针
}
public:
T _val;
};
holder *_content;///holder类型的指针(基类类型的指针) 是Any类的私有成员变量
//用于存储实际的placeholder对象 从而实现存储任意类型数据的功能
public:
默认构造函数
Any():_content(NULL){}
//类模板构造函数
template<class T>
Any(const T &val) //直接给数据构造通用容器
:_content(new placeholder<T>(val)) //将val存储在Any容器中
{}
//类的拷贝构造函数 //通过其他的容器构造新的容器
Any(const Any &other) //
:_content(other._content ? other._content->clone() : NULL)
//如果other._content不为空,就调用clone()克隆other对象 并将新对象的指针赋给_content 否则将 _content 赋值为 NULL
{}
~Any()
{delete _content;}
Any &swap(Any &other)
{
std::swap(_content, other._content);
return *this; //返回当前对象的引用
}
template<class T>
T *get() //获取保存对象的类型 可以是任意类型 返回子类对象保存的数据的指针
{
//想要获取的数据类型,必须和保存的数据类型一致
assert(typeid(T) == _content->type());
return &((placeholder<T>*)_content)->_val; //将 _content 指针转换为 placeholder<T>* 类型,然后返回存储数据 _val 的地址
}
//各种通用容器给容器赋值重载=运算符
template<class T>
Any& operator=(const T &val)
{
//为val构造一个临时通用容器,然后与当前容器自身进行指针交换,临时对象释放的时候,原先保存的数据也就被释放了
Any(val).swap(*this);
return *this;
}
Any& operator=(const Any &other)
{
Any(other).swap(*this);
return *this;
}
};
class Test
{
public:
Test() { std::cout<<"构造" << std::endl;}
Test(const Test &t) { std::cout << "拷贝" << std::endl;}
~Test() { std::cout<<"析构" << std::endl;}
};
int main()
{
//std::any a;
//a = 10;
//int *pi = std::any_cast<int>(&a);
//std::cout << *pi << std::endl;
//a = std::string("hello");
//std::string *ps = std::any_cast<std::string>(&a);
//std::cout << *ps << std::endl;
Any a;
{
Test t;
a = t;
}
a = 10;
int *pa = a.get<int>();
std::cout << *pa << std::endl;
a = std::string("nihao");
std::string *ps = a.get<std::string>();
std::cout << *ps << std::endl;
return 0;
}