簡介
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; //輸出錯誤信息
}
}