C++ regex库常用函数及实例
简介
regex是C++标准库中用于正则表达式(regular expression)的部分。
大致有如下常用组件:
组件名称 | 作用 |
---|---|
regex | 表示有一个正则表达式的类 |
regex_match | 将一个字符序列与一个正则表达式匹配 |
regex_search | 寻找第一个与正则表达式匹配的子序列 |
regex_replace | 使用给定格式替换一个正则表达式 |
sregex_iterator | 迭代器适配器,内部调用regex_search来遍历一个string中所有匹配的子串 |
smatch | 容器类,保存在string中搜索的结果 |
ssub_match | string中匹配的子表达式的结果 |
示例
在下面一个简单的例子里使用一些组件
#include<regex>
#include<iostream>
using namespace std;
//正则表达式
void main()
{
string pattern = "^([a-z]|_)[[:alnum:]]+";
//开头的^表示从字符串开头开始匹配,|表示或,alnum表示字母或数字,+表示至少重复一次
regex r(pattern, regex::icase);//初始化正则表达式类,icase表示忽略大小写
string s("Asff");
smatch results;//用于保存成功匹配的相关信息
if (regex_search(s, results, r))
cout << results.str() << endl;
s="_qwer";
if (regex_search(s, results, r))
cout << results.str() << endl;
s="9sff";
if (regex_search(s, results, r))
cout << results.str() << endl;
}
输出如下:
Asff
_qwer
在这个例子里,我们通过regex_search函数,查找第一个与正则表达式匹配的子序列,smatch对象将会保存匹配结果的相关细节。
异常
C++的正则表达式并不是由C++编译器解析,而是在运行时由相关库函数进行解析,因此如果正则表达式存在语法错误,程序将会抛出名为regex_error的异常。
捕获异常并且输出错误信息:
void fun()
{
try{
regex r("([[:alpha:]]");
}catch(regex_error e)
{
cout<<e.what()<<"\n";//错误信息
cout<<e.code()<<endl;//错误码
}
}
char数组传参
regex_search函数的输入序列参数可以传入string或者以'\0'结尾的字符数组,传入string时,使用smatch对象接受匹配成功的相关信息;而传入char*时,如果还使用smatch对象就会编译失败,此时需要使用cmatch对象才能编译成功。
使用regex迭代器来获取所有匹配
sregex_iterator的部分操作如下表
操作 | 作用 |
---|---|
sregex_iterator it(b,e,r); | b,e分别为输入序列的迭代器起始尾后位置,将sregex_iterator对象it定位到输入中第一个匹配的位置 |
sregex_iterator it_end; | 无参构造函数生成尾后迭代器 |
*it | 解引用,根据最后一个调用regex_search的结果,返回一个smatch对象的引用 |
it-> | 间接引用smatch的成员函数 |
++it | 在当前匹配位置调用regex_search,并返回递增后的迭代器 |
it++ | 在当前匹配位置调用regex_search,但返回递增前的迭代器 |
it1==it2 | 如果都是尾后迭代器,则相等。非尾后迭代器如果由相同的输入序列和相同的regex对象构造,则相等 |
it1!=it2 | 不符合相等的情况 |
综合示例
综合上面的知识,我们可以编写一个提取合法ipv4地址的小程序,其中regex对象pattern中使用\\的原因是:一个\用于转义'('、')'、'd'(表示整数)等符号,另一个是由于C++中\为转义字符,\\才表示一个\符号。
#include<regex>
#include<iostream>
using namespace std;
bool validSubExepression(const smatch& s)
{//检查表达式是否合法
if (s[1].matched)//如果有左括号,那么要求一定要有匹配的右括号
return s[9].matched && s[3].str() == s[5].str() && s[5].str() == s[7].str();//且3个分隔符要相同
else//如果没有左括号,那么要求没有右括号
return !s[9].matched && s[3].str() == s[5].str() && s[5].str() == s[7].str();
}
bool overflow(const smatch& s)
{//ip地址是否溢出
for (int i = 2; i <= 8; i++)
{
int number = atoi(s.str().c_str());
if (number > 255)
return true;
}
return false;
}
int main()
{
string pattern =
"(\\()?(\\d{1,3})([-. ])?(\\d{1,3})([-. ])?(\\d{1,3})([-. ])?(\\d{1,3})(\\))?";
string ipaddress = "192.168.1.2 (123.233.111.33 114.114.114.114) (8-8.8.8) 7-8-9-10 172 0 0 1 888.224.525.244 192&168&1&1";
string fmt = "($2.$4.$6.$8)";//格式化,子表达式2,4,6,8原样输出,其余部分按(...)格式输出
try
{
regex r(pattern);
for (sregex_iterator it(ipaddress.begin(), ipaddress.end(), r), end_it; it != end_it; ++it)
{
if (!overflow(*it))
{
if (validSubExepression(*it))
{
cout << "Before format:" << it->str() << endl;
cout << "After format:" << regex_replace(it->str(), r, fmt) << endl << endl;
}
else
{
cout << "not valid:" << endl;
cout << "Before format:" << it->str() << endl;
cout << "After format:" << regex_replace(it->str(), r, fmt) << endl << endl;
}
}
else
cout << "overflow" << endl;
}
}
catch (regex_error e)
{
cout << "(error code:" << e.code() << ")" << endl;//输出错误代码
cout << e.what() << endl; //输出错误信息
}
}