1到千、介紹
正則表達(dá)式是一種描述字符序列的方法,下面介紹C++11正則表達(dá)式庫(kù)(RE)庫(kù)的使用。
其包含的組件如下:
regex //表示一個(gè)正則表達(dá)式的類(lèi)
regex_match //將一個(gè)字符與一個(gè)正則表達(dá)式匹配
regex_search //尋找第一個(gè)與正則表達(dá)式匹配的子序列
regex_replace //使用給定格式替換一個(gè)正則表達(dá)式
sregex_iterator //迭代器適配器,調(diào)用regex_search來(lái)遍歷一個(gè)string中所有匹配的字串
smatch //容器類(lèi)浪感,保存在string中搜索的結(jié)果
ssub_match //string中匹配的子表達(dá)式的結(jié)果
2、正則表達(dá)式的使用
從一個(gè)例子開(kāi)始饼问,查找違反拼寫(xiě)規(guī)則:i除非在c之后影兽,否則必須在e之前的單詞。
string pattern("[^c]ei");
pattern = "[[:alpha:]]*" + pattern + "[[:alpha:]]*";
regex r(pattern);
smatch ret;
string reg_test = "receipt freind theif receive";
if (regex_search(reg_test, ret, r))
cout << "find the str: " <<ret.str() << endl;
首先聲明我們需要使用的pattern匆瓜,即正則表達(dá)式赢笨;然后定義regex 和smatch ,調(diào)用regex_search函數(shù)查找對(duì)應(yīng)的字符串中滿(mǎn)足pattern的字串驮吱。然后調(diào)用smatch.str()輸出茧妒。C++11中的正則表達(dá)式默認(rèn)使用的正則表達(dá)式語(yǔ)法是ECMAScript。
下面列出regex常用的一些選項(xiàng):
regex r(re) //re可以是一個(gè)正則表達(dá)式左冬,一個(gè)string桐筏,一個(gè)表示字符范圍的迭代器對(duì),一個(gè)指向空字符結(jié)尾的字符數(shù)組的指針拇砰,一個(gè)字符指針和一個(gè)計(jì)數(shù)器或者是一個(gè)花括號(hào)包圍的字符列表
regex r(re, f) //f是指出對(duì)象如何處理的標(biāo)志
r = re //將r中正則表達(dá)式替換為re
r.assign(re, f); //與運(yùn)算符 = 效果相同
r.mark_count(); //r中子表達(dá)式的數(shù)目
r.flags(); //返回r的標(biāo)志集合
下面是標(biāo)志集合:
icase 忽略大小寫(xiě)
nosubs 不保存匹配的子表達(dá)式
optimize 執(zhí)行速度優(yōu)先于構(gòu)造速度
ECMAScript 使用ECMA-262語(yǔ)法
basic 使用POSIX基本的正則表達(dá)式語(yǔ)法
extended 使用POSIX擴(kuò)張的正則表達(dá)式語(yǔ)法
awk 使用POSIX版本的awk語(yǔ)言的語(yǔ)法
grep 使用POSIX版本的grep語(yǔ)言的語(yǔ)法
grep 使用POSIX版本的egrep語(yǔ)言的語(yǔ)法
接下來(lái)再看一個(gè)例子梅忌,查找后綴為.cpp .cc. cxx的文件:
array<string, 5> as5{"1.cpp", "sdja.txt", "qwei91.cxx", "sdauhd.c", "asdjh.cc"};
regex r1("([[:alnum:]]+)\\.(cpp|cxx|cc)$", regex::icase); //在第一個(gè)表達(dá)式中加了()就變成了子表達(dá)式
smatch ret1;
for (auto e : as5)
{
if(regex_search(e, ret1, r1))
cout << "find the cpp\\cxx\\cc file: " << ret1.str() << endl;
}
在編寫(xiě)正則表達(dá)式的時(shí)候如果寫(xiě)錯(cuò)了,比如缺少括號(hào)這樣的錯(cuò)誤除破,在編譯階段是不會(huì)報(bào)錯(cuò)的牧氮,因?yàn)檎齽t表達(dá)式本身相當(dāng)于一個(gè)簡(jiǎn)單語(yǔ)言設(shè)計(jì)的“程序”。所以如果正則表達(dá)式寫(xiě)錯(cuò)了瑰枫,只會(huì)在運(yùn)行階段踱葛,拋出異常。C++11中定義的正則表達(dá)式異常有13個(gè)光坝,用到的時(shí)候再去查吧尸诽。
3、匹配與Regex迭代器類(lèi)型
在使用正則表達(dá)式匹配字符串的時(shí)候盯另,我們可以使用sregex_iterator來(lái)獲得所有的匹配性含。
下面是sregex_iterator的操作:
sregex_iterator it(b,e,r); //遍歷迭代器b和e表示的string,它調(diào)用sregex_search(b,e,r)將it定位到輸入中第一個(gè)匹配的位置
sregex_iterator end; //尾后迭代器
*it //返回一個(gè)smatch對(duì)象的引用
it-> //指向smatch對(duì)象的指針
++it //自加
it++
it1 == it2; //如果兩個(gè)sregex_iterator都是尾后迭代器鸳惯,則它們相等商蕴。兩個(gè)非尾后迭代器是從相同的輸入序列和regex對(duì)象構(gòu)造叠萍,則它們相等
it1 != it2;
當(dāng)我們將一個(gè)sregex_iterator綁定到一個(gè)string和一個(gè)regex對(duì)象時(shí),迭代器自動(dòng)定位到給定string中第一個(gè)匹配位置绪商。
下面是一個(gè)使用sregex_iterator的例子:
//使用regex_iterator,輸出所有的匹配結(jié)果,并且輸出上下文
string pattern1("[^c]ei");
pattern1 = "[[:alpha:]]*" + pattern1 + "[[:alpha:]]*";
regex r2(pattern1, regex::icase);
string reg_test = "receipt freind theif receive";
for (sregex_iterator it(reg_test.begin(), reg_test.end(), r2), end_it; it != end_it; ++it)
{
auto pos = it->prefix().length();
pos = pos > 40 ? pos - 40 : 0;
cout << it->prefix().str().substr(pos)
<< "\n\t\t>>>" << it->str() << "<<<\n"
<< it->suffix().str().substr(0, 40) << endl;
}
上面的例子中使用sregex_iterator輸出所有匹配的結(jié)果俭令,并且使用prefix()和suffix()輸出匹配位置的上下文。
下面是smatch的常用操作:
m.ready(); //如果已經(jīng)通過(guò)調(diào)用regex_serach或者regex_match設(shè)置了m部宿,則返回true抄腔;否則返回false。如果ready返回false理张,則對(duì)m的操作是未定義的
m.size(); //如果匹配失敗赫蛇,則返回0;否則返回最近一次匹配的正則表達(dá)式中子表達(dá)式的數(shù)目
m.empty(); //若m.size()為0雾叭,則返回true
m.prefix(); //一個(gè)ssub_match對(duì)象悟耘,表示當(dāng)前匹配之前的序列
m.suffix(); //一個(gè)ssub_match對(duì)象,表示當(dāng)前匹配之后的序列
m.format(...);
m.length(n); //第n個(gè)匹配的子表達(dá)式的大小织狐,0表示整個(gè)匹配
m.position(n); //第n個(gè)子表達(dá)式距離序列開(kāi)始的距離
m.str(n); //第n個(gè)子表達(dá)式匹配的string
m[n]; //第n個(gè)匹配的子表達(dá)式的ssub_match對(duì)象
m.begin(), m.end(); //表示匹配范圍的迭代器
m.cbegin(), m.cend(); //表示匹配范圍的常迭代器
4暂幼、使用匹配的子表達(dá)式
字符串使用正則表達(dá)式匹配之后有許多的子表達(dá)式,常用于進(jìn)行數(shù)據(jù)驗(yàn)證或者格式轉(zhuǎn)換移迫。
看下面的例子:
bool phone_valid(const smatch& m)
{
//如果區(qū)號(hào)前有一個(gè)左括號(hào)
if (m[1].matched)
//return m[3].matched && (m[4].matched == 0 || m[4].str() == " ");
return m[3].matched;
else
//return !m[3].matched && m[4].str() == m[6].str();
return !m[3].matched;
}
//使用正則表達(dá)式檢查電話(huà)號(hào)碼是否合法
string reg_phone = "(\\()?(\\d{3})(\\))?([-. ])?(\\d{3})([-. ]?)(\\d{4})";
regex r3(reg_phone);
//smatch m;
string reg_s = "0201234567, 020.1234567, 020-1234567, 020-123456789, (020)-1234567, (020).1234567, (020) 1234567, (020)-12345678910, (0201234567, 020)1234567, 12345678910, 020s1234567, 020 s123456, 020/1234657, +86 1234567, +86.1234567, +86-1234567";
for (sregex_iterator it(reg_s.begin(), reg_s.end(), r3), end_it; it != end_it; ++it)
{
if(phone_valid(*it))
cout << "valid: " << it->str() << endl;
else
cout << "not valid: " << it->str() << endl;
}
使用regex_replace旺嬉,這個(gè)對(duì)象可以用于替換字符串中的特定字符。
m.format(dest, fmt, mft)
m.format(fmt, mft);
regex_replace(dest, seq, r, fmt, mft);
regex_replace(seq, r, fmt, mft);
上面的幾個(gè)操作是regex_replace的常用操作厨埋,光看命名就很好理解邪媳。
下面用一個(gè)例子直接展示regex_replace的使用:
string reg_phone = "(\\()?(\\d{3})(\\))?([-. ])?(\\d{3})([-. ]?)(\\d{4})";
string reg_replace_test = "(654) 123-7856";
regex reg_replace(reg_phone);
string regex_replace_fmt = "$2-$5-$7";
cout << regex_replace(reg_replace_test, reg_replace, regex_replace_fmt) << endl;
上述例子將電話(huà)的格式轉(zhuǎn)換成用 - 連接的形式。
標(biāo)準(zhǔn)庫(kù)中定義了標(biāo)志來(lái)直到如何處理正則表達(dá)式荡陷,在替換過(guò)程中也是一樣的雨效,定義了標(biāo)志來(lái)控制匹配或格式的標(biāo)志。這些標(biāo)志可以傳遞給函數(shù)regex_search或regex_match或是類(lèi)smatch的fomat成員废赞。這些值定義在命名空間std::regex_constants中徽龟。
match_default 等價(jià)于format_default
match_not_bol 不將首字符作為行首處理
match_not_eol 不將尾字符作為行首處理
match_not_bow 不將首字符作為單詞首處理
match_not_eow 不將尾字符作為單詞尾處理
match_any 如果存在多個(gè)匹配,則返回任意一個(gè)匹配
match_not_null 不匹配任何空序列
match_continuous 匹配必須從輸入的首字符開(kāi)始
match_prev_avail 輸入序列包含第一個(gè)匹配之前的內(nèi)容
format_default 用ECMAScript規(guī)則替換字符串
format_sed 用POSIX sed規(guī)則替換字符串
format_no_copy 不輸出輸入序列中未匹配的部分
format_first_only 只替換子表達(dá)式的第一次出現(xiàn)的
如修改上面的替換例子:
string reg_phone = "(\\()?(\\d{3})(\\))?([-. ])?(\\d{3})([-. ]?)(\\d{4})";
string reg_replace_test = "(654) 123-7856";
regex reg_replace(reg_phone);
string regex_replace_fmt = "$2-$5-$7";
cout << regex_replace(reg_replace_test, reg_replace, regex_replace_fmt, format_no_copy ) << endl;
這樣只會(huì)輸出regex_replace拷貝的文本唉地,而不會(huì)輸出整個(gè)輸入序列据悔。
正則表達(dá)式的使用建議
(1)避免創(chuàng)建不必要的正則表達(dá)式,正則表達(dá)式的編譯是一個(gè)非常慢的操作渣蜗,運(yùn)行時(shí)開(kāi)銷(xiāo)也比較大屠尊。
(2)正則表達(dá)式的的類(lèi)型要與輸入的類(lèi)型匹配旷祸,如輸入類(lèi)型是string耕拷,使用正則表達(dá)式的類(lèi)為regex, smatch等;如輸入類(lèi)型為const char,使用正則表達(dá)式的類(lèi)為:regex托享,cmatch等骚烧;如輸入類(lèi)型為wstring,使用正則表達(dá)式的類(lèi)為:wregex浸赫,wsmatch等;如輸入類(lèi)型為const wchar_t,使用正則表達(dá)式的類(lèi)為:wregex赃绊,wcmatch等既峡;
總結(jié)
C++11新標(biāo)準(zhǔn)引入了正則表達(dá)式庫(kù),在C++代碼里面可以直接使用標(biāo)準(zhǔn)庫(kù)的正則表達(dá)式工具碧查,對(duì)于字符串的處理非常方便运敢。但是因?yàn)檎齽t表達(dá)式的開(kāi)銷(xiāo)比較大,并且在編譯階段是不會(huì)報(bào)錯(cuò)的忠售,所以應(yīng)該避免不必要的使用以及做好異常處理传惠。