序言
正则表达式,简而言之就是用来匹配指定模式字符串的工具,在计算机的世界中,它发挥着很大的作用,比如编译器的词法分析,注册时验证密码的复杂度,爬虫爬取固定格式的数据时等场景都要用到。那么本篇的目的就是能够让大家快速上手正则表达式,并学会相关的接口。下面将先从使用规则入手,先上手正则表达式,再介绍C++11库中给出的接口,编写出能够匹配指定规则字符串的正则表达式,因此就算不懂C++也能学会使用正则表达式。
一、基本规则
测试工具
推荐一个方便进行正则表达式测试的网站:https://regex101.com,这一个就够了。
说明:在Vscode下按下 CTRL + F,以及网页代码的编写界面中 也可以使用正则表达式进行测试,感兴趣的读者可自行了解。
先介绍一下使用此网站匹配的效果,即当输入字符串时,如果匹配正则表达式的内容,则会以高亮的形式显示出来,如下图。
限定符
- 一般来说,限定符限定是前面的字符的出现次数,通常使用的有
+,*,?,{n,m},{n,} ,{n}
。
先来看+
,表示限制前面的字符出现1次或者多次。举个例子,当使用a+
去匹配aabb
时,aa
的部分会以高亮的形式显示出来,如下图。
再来看*
,表示前面的字符出现任意次,比上面的+的区别是可以出现0次。举个例子,当使用ca*bb
去匹配cbb
时,cbb
会以高亮的形式显示出来,原因就在于a*,表示a可以出现0次,效果如下图。
接着来看?
,表示前面的字符出现1次或者0次。,当使用ca?bb
去匹配cbb
时,cbb
会以高亮的形式显示出来,效果如下图。
最后来看,{n,m},{n,} ,{m}
分别表示前面的字符出现n到m次,至少出现n
次,m
次,可以帮助更好地控制字符的出现次数。举个例子,使用的匹配字符串为aaaaa
,当分别使用a{3}
,a{3,}
,a{3,4}
匹配时,分别为a出现3次,a出现至少3次,a出现3次到4次。效果如下图。
除此之外,这里再多介绍一个基本上是万能的字符.
,即表示匹配除换行符之外的任意一个字符。比如说hell. world
,就可以完全匹配hello world
,效果如下图,并且一般来说常见的组合是.*
即表示匹配除换行符之外的字符任意次。
介绍到这里你或许会觉得正则表达式这么简单么,当然不是,上面所说的仅仅是很简单的用法而已,还要搭配着其它的正则表达式的字符进行使用才有更高阶的玩法,下面介绍一些可以进行搭配使用的字符。
定位符
常见的一般来说有^
和$
,分别表示匹配行首的字符串和行尾的字符串,这个十分简单,举个例子,分别使用 ^aa和aa\$
分别去匹配aaaa
,分别只会匹配前面的aa
和后面的aa
,效果如下图。
表达式
常见的一般来说有()
为子表达式,存放的是指定要求的匹配模式,[]
为中表达式,存放的是字符的范围。
首先来看()
,光看理论是不容易理解的,直接看例子,比如说(aa)?bb
,其中aa
表示可以匹配0次或者一次,去匹配aabb
和bb
,都能匹配成功,效果如下图。
除此之外,这里再介绍一个逻辑字符 |
,表示或的意思,配合()可以用来匹配指定条件的字符串,比如(aa|ac)bb
,就可以匹配aabb
以及acbb
,效果如下图,在筛选出固定后缀的字符串时比较适用。
再来看[]
,同理还是直接看例子,比如b[az]c
,就可以匹配bac
以及bzc
,效果如下图。
除此之外,这里也要多介绍一个^
表示逻辑取反的意思,比如a[^be]c
,可以匹配除了abc和aec之外的a.c
模式的字符串,效果如下图。
补充:除了放指定满足要求的字符外,还可以放字符的范围,比如[a-z]表示所有的小写字母,[0-9]表示所有的数字字符,等等。
等价替换
在正则表达式中,[]
中存放表示范围的字符或许太常用了,比如[a-z]
,[0-9]
,所以采用了一些特殊字符将其等价替换,常见的有\d
,对应着[0-9]
,与之相反的是\D
,对应着[^0-9]
, \w
对应着[a-z]
,与之相反的是\W
对应着[^a-z]
,\s
对应着 [ \f\n\r\t\v]
,即可以匹配任意的空格符,制表符,换行符等,与之相反的是\S
,对应着[^\f\n\r\t\v]
。举个例子,比如\d\w\s,即可匹配小写字母字符 + 数字字符 + 换行符
默认的字符串,效果如下图。
读到了这里相信各位已经对正则表达式有了一个基本的认识,恭喜你简单入门了! 不过还需各位进行实践解锁更多玩法,下面将介绍C++11提供的正则表达式的基本接口。
二、接口介绍
C++11中引入了<regex>
库,即引入了关于正则表达式的调用接口的库,主要包含std::regex
类用于存放正则表达式,std::smatch
类用于存放匹配的结果,std::regex_match
进行全文匹配,std::regex_search
进行搜索匹配项,std::regex_replace
用于替换与正则表达式匹配的字符串。展开来讲还有很多,感兴趣的读者可自行到官方文档——cplusplus了解。
1.正则表达式
regex,即正则表达式对象,用于设置目标字符串的正则表达式,用字符串进行初始化和赋值,里面包含一些接口mark_count
获取子表达式的数量,flags
用于设置匹配的模式,imbue与getloc
设置与获取区域设置等等。
说明:
- 子表达式,简单理解为正则表达式中一对
()
即为一个子表达式。 - 匹配模式,比如设置为
icase
,即为忽略大小写。 - 区域设置,与时间,地区,语言,字符集等内容相关。比如设置UTF-8,GBK,ASCII编码等不同的字符集。
2.容器
smatch
,存放匹配结果的容器,用于匹配string
类型的对象,类似的还有cmatch
。- 如果没有匹配成功则为空;如果匹配成功,下标为0处存放的是匹配的结果,下标为1处存放的是第一个子表达式,下标为2处存放的是第二个子表达式,以此类推。
- 常用的接口有
prefix
与suffix
分别用于获取匹配结果的前缀和后缀,length
与position
分别用于获取指定下标处的字符串的长度和在原字符串的位置,str
和[]
用于获取指定下标处的匹配结果,empty
和ready
分别用于检测是否为空和是否在之前被调用过。
3.全文匹配
- 函数名——regex_match
- 参数
- string,匹配的字符串。
- smatch,输出型参数,存放匹配的结果。
- regex,正则表达式对象。
- 返回值,匹配成功为true,反之为false。
demo
#include<iostream>
#include<regex>
#include<string>
using namespace std;
int main()
{
smatch res;//存放结果
string str = "subject";
string pattern("(sub)(.*)");
regex r(pattern);
bool ret = regex_match(str,res,r);
if (ret)
{
cout << "匹配成功" << endl;
cout << "匹配的结果为:" << res[0] << endl;
cout << "第一个子表达式为:" << res[1] << endl;
cout << "第二个子表达式为:" << res[2] << endl;
}
else
{
cout << "匹配失败" << endl;
}
return 0;
}
output
- 拓展:使用正则表达式获取解析Http协议字段
#include <iostream>
#include <string>
#include <regex>
#include <iterator>
using namespace std;
int main()
{
//全文匹配,使用子表达式存放解析结果,只提取出请求方法,域名信息,查询字段(可能有或者没有)。
smatch res;
string str = "GET /blog.csdn.net/Shun_Hua?user=xiaoming&pass=123123 HTTP/1.1\r\n";
string pattern("(GET|HEAD|POST|PUT|DELETE) ([^?]*)(?:\\?(.*))? HTTP/1\\.[01](?:\r\n|\n)?");
//(GET|HEAD|POST|PUT|DELETE)请求方法的匹配。
//([^?]*)表示匹配非问号字符串,用于获取域名。
//\\?,首先?是特殊字符,要想正常匹配得使用\进行转义,\也是特殊转义字符,要想使用得再使用\进行转义。
//(.*),.*是对任意字符串进行匹配。
//?:\\?,表示不提取?到res中
//(\\?(.*))?,表示子表达式出现0次或者1次。
//(?:\\?(.*))?,子表达式出现0次或者1次,不提取?,提取(.*)到res中。
//HTTP/1\\.[01],匹配版本号。
//(?:\r\n|\n)?,同理表示匹配\r\n或者\n,0次或者1次,且不提取。
regex r(pattern);
bool ret = regex_match(str, res, r);
if (ret)
{
cout << "匹配成功" << endl;
for (auto e : res)
{
cout << e << endl;
}
}
else
{
cout << "匹配失败" << endl;
}
return 0;
}
output
4.搜索
- 函数名——regex_search
- 参数
- string,匹配的字符串。
- smatch,输出型参数,存放匹配的结果。
- regex,正则表达式对象。
- 返回值,匹配成功为true,反之为false。
demo:
#include <iostream>
#include <string>
#include <regex>
int main()
{
string str("this subject has a marine.");
smatch res;//存放结果
regex r("subject");
bool ret = regex_search(str, res, r);
if (ret)
{
cout << "匹配成功" << endl;
cout << "匹配的结果为:" << res[0] << endl;
cout << "前缀为:" << res.prefix() << endl;
cout << "后缀为:" << res.suffix() << endl;
}
else
{
cout << "匹配失败" << endl;
}
return 0;
}
output:
5.替换
- 函数名——regex_replace
- 参数
- string,匹配的字符串。
- regex,正则表达式对象。
- string,用于替换的字符串。
- 返回值,匹配成功为true,反之为false。
demo
#include <iostream>
#include <string>
#include <regex>
#include <iterator>
using namespace std;
int main()
{
string s("there is subject in the string\n");
regex e("subject");
string rep = "something";
cout << std::regex_replace(s, e, rep) << endl;
return 0;
}
output
尾序
本篇介绍了正则表达式的基本使用以及C++11中提供的接口,总的来说入门正则表达式并不难,难的是如何活学活用,剩下就靠各位读者实践解锁更多玩法了,我是舜华,期待与你的下一次相遇!