C++11:正則表達(dá)式

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)該避免不必要的使用以及做好異常處理传惠。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市稻扬,隨后出現(xiàn)的幾起案子卦方,更是在濱河造成了極大的恐慌,老刑警劉巖泰佳,帶你破解...
    沈念sama閱讀 217,185評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件盼砍,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡逝她,警方通過(guò)查閱死者的電腦和手機(jī)浇坐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)黔宛,“玉大人吗跋,你說(shuō)我怎么就攤上這事∧眩” “怎么了跌宛?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,524評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀(guān)的道長(zhǎng)积仗。 經(jīng)常有香客問(wèn)我疆拘,道長(zhǎng),這世上最難降的妖魔是什么寂曹? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,339評(píng)論 1 293
  • 正文 為了忘掉前任哎迄,我火速辦了婚禮,結(jié)果婚禮上隆圆,老公的妹妹穿的比我還像新娘漱挚。我一直安慰自己,他們只是感情好渺氧,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,387評(píng)論 6 391
  • 文/花漫 我一把揭開(kāi)白布旨涝。 她就那樣靜靜地躺著,像睡著了一般侣背。 火紅的嫁衣襯著肌膚如雪白华。 梳的紋絲不亂的頭發(fā)上慨默,一...
    開(kāi)封第一講書(shū)人閱讀 51,287評(píng)論 1 301
  • 那天,我揣著相機(jī)與錄音弧腥,去河邊找鬼厦取。 笑死,一個(gè)胖子當(dāng)著我的面吹牛管搪,可吹牛的內(nèi)容都是我干的虾攻。 我是一名探鬼主播,決...
    沈念sama閱讀 40,130評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼更鲁,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼台谢!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起岁经,我...
    開(kāi)封第一講書(shū)人閱讀 38,985評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤朋沮,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后缀壤,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體樊拓,經(jīng)...
    沈念sama閱讀 45,420評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,617評(píng)論 3 334
  • 正文 我和宋清朗相戀三年塘慕,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了筋夏。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,779評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡图呢,死狀恐怖条篷,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情蛤织,我是刑警寧澤赴叹,帶...
    沈念sama閱讀 35,477評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站指蚜,受9級(jí)特大地震影響乞巧,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜摊鸡,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,088評(píng)論 3 328
  • 文/蒙蒙 一绽媒、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧免猾,春花似錦是辕、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,716評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春石窑,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蚓炬。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,857評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工松逊, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人肯夏。 一個(gè)月前我還...
    沈念sama閱讀 47,876評(píng)論 2 370
  • 正文 我出身青樓经宏,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親驯击。 傳聞我的和親對(duì)象是個(gè)殘疾皇子烁兰,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,700評(píng)論 2 354