C++正則表達式


轉載請說明出處。

C++正則

總述

正則是一種規則,它用來匹配(進而捕獲、替換)字符串。這種規則需要“模式”、“字符串”這兩樣東西,“模式”根據正則規則,來處理“字符串”。

這種規則被許多語言支持,C++11以后才支持正則。

C++11支持的正則和其他語言支持的正則有區別,本篇記錄的目的不在於講解正則,也不在於闡述C++11支持的正則詳細是什么,而是只記下常見的、夠用的正則規則(以供速查,應有不嚴謹之處)及使用用法。

ECMAScript支持的正則

正則由元字符和普通字符組成。普通字符就代表它原本的含義;元字符的意義不同於該字符本來的含義,而是有特殊的意義和功能。

根據其意義功能划分,可將元字符划分為:

具有特殊意義的元字符

\\字符能夠改變字符原本的含義

^^字符指示字符串的頭,且要求字符串以字符開頭,不占位。\^表示一個真正的^符號。

$$字符指示字符串的尾,且要求字符串以字符結尾,不占位。\$表示一個真正的$符號。

():分組,大正則中包含小正則。可以改變默認的優先級。在模式中可以使用\1來表示第一組已然捕獲到的東西。

\b:指示字符串的邊界(頭/尾/空格左/空格右),字符\b要求邊界的左邊是字符\b字符要求邊界的右邊是字符

.:表示一個除了\n以外的任意一個字符。\.表示一個真正的.符號。

|字符串1|字符串2表示一個字符串,該字符串是字符串1、字符串2中的一個。|在正則中的優先級比較混亂,所以建議加上足夠多的括號來分組。

[][字符1字符2字符3...]表示一個字符,該字符是字符1、字符2、字符3……中的某一個。中括號中出現的所有字符都是代表本身意思的字符(沒有特殊含義),如[.]只能匹配.符號,而不能匹配任意符號。

[^字符1字符2字符3...]表示一個字符,該字符不是字符1、字符2、字符3……中的任何一個

[a-z]表示一個字符,該字符是a、b、c……z中的某一個

[^a-z]表示一個字符,該字符不是a、b、c……z中的任何一個

\w:表示一個字符,該字符是數字、字母、下划線中的某一個。等價於[(0-9)(a-z)(A-Z)(_)]

\W:表示一個字符,該字符不是數字、字母、下划線中的任何一個。等價於[]

\d表示一個字符,該字符是0、1、2……9中的某一個

\D表示一個字符,該字符不是0、1、2……9中的任何一個

\s表示一個字符,該字符是空白符(空格、制表符、換頁符)2、代表出現次數的

量詞元字符

*字符*要求字符出現0到多次

+字符+要求字符出現1到多次

?字符?要求字符出現0次或1次

{n}字符{n}要求字符出現n次

{n,}字符{n,}要求字符出現n到多次

{n,m}字符{n,m}要求字符出現n到m次、

C++支持的正則

C++可以支持ECMAScript支持的正則,也可以支持grep支持的正則等等。

由於我本人較熟悉ECMAScript支持的正則,且C++默認支持的即是它,所以我接下來講的也是這種正則。

首先要舉一個例子解釋一下,

/**在ECMAScript中寫一個匹配11位電話號碼的正則模式是這樣寫的**/
var r = /\d{1, 11}/;
/**在C++中寫一個匹配11位電話號碼的正則模式是這樣寫的**/
regex r("\\d{1, 11}")

看似不一樣,其實是一樣的,因為傳遞給regex構造函數的參數是一個字符串,該字符串被C++編譯器解析時,將\\解釋為真正的\(由於C++中的字符\是轉義字符),解析出來的\和緊隨其后的d組合,形成了真正的\d,表示一個數字字符。

所以含有\的元字符,在C++定義時,都要寫成\\

C++使用正則

匹配與否

頭文件<regex>中的regex_matchregex_search均可以進行匹配,返回一個布爾類型,匹配成功為true,匹配失敗為false。

不同點:前者要求完全匹配,后者要求子串匹配即可;

下面以regex_match為例,regex_search的寫法與之相同,故不贅述。

string str = "hhh233";
regex r("[a-z0-9]+");

// 用法一
bool flag = regex_match(str,r);
// 用法二
bool flag = regex_match(str,regex("\\d+"));
// 用法三
bool flag = regex_match(str.begin()+7,str.end(),regex("\\d+"));

捕獲

捕獲就是先匹配,然后將匹配結果存儲下來。捕獲同樣是使用上面介紹的那兩個函數,仍然區分為整串匹配和子串匹配。

捕獲的步驟:

  1. 模式中一般要有分組(因為捕捉的正是分組匹配的結果)
  2. 定義一個STL容器smatch,用來保存捕捉的結果
  3. 使用reg_search函數匹配,用smatch的實例存儲匹配的結果,即完成捕捉。
  4. 使用m.size(),查看捕捉到的個數;使用m.str(i),查看捕捉到的字符串;【注意:m.str(0)一定是整個正則匹配到的部分,m.str(1)及以后才是分組捕獲的結果】
  5. m.prefix().str()獲取整個匹配之前的字符串;m.suffix().str()獲取整個匹配之后的字符串

代碼:

#include <bits/stdc++.h>
using namespace std;
int main()
{
    string str;
    while(true){
        cin >> str;
        regex e("([[:w:]]+)@([[:w:]]+)\.com");
        smatch m;
        bool found = regex_search(str, m, e);
        if(found)
        {
            cout << "m.size() " << m.size() << endl;
            for(int i=0; i<m.size(); ++i){
                cout << "m.str(" << i << "): " << m.str(i) << endl;
            }
            cout << "m.prefix().str(): " << m.prefix().str() << endl;
            cout << "m.suffix().str(): " << m.suffix().str() << endl;
        }
        else cout << "Not Found" << endl;
        return 0;
        
        
    }
}

結果為:

補充:

  1. 獲取第i個匹配m.str(i),還有其他等價寫法:m[i].str()*(m.begin() + i)
  2. 模式其實可以不分組...這樣就只有m.str(0)捕捉到整個串,沒有m.str(1)其他的了。

更高級的捕捉

由於regex_replace方法只能捕捉到第一個匹配,想要捕捉到每一個匹配,以及匹配內的分組,需要使用另外一種方法:

步驟:

  1. 正則內有分組
  2. 定義迭代器的同時初始化迭代器,使其指向一個smatch實例的數組。
  3. 定義end,標志smatch實例的數組的末尾
  4. 使用迭代器遍歷數組

代碼:

#include <bits/stdc++.h>
using namespace std;
int main()
{
    string str = "boo@gmail.com  boqian@hotmail.com bo_qian@163.com";
    regex e("([[:w:]]+)@([[:w:]]+)\.com");
    sregex_iterator pos(str.cbegin(), str.cend(), e);
    sregex_iterator end;
    
    for(; pos!=end; ++pos)
    {
        cout << "Matched: " << pos->str(0) << endl;
        cout << "user name: " << pos->str(1) << endl;
        cout << "domain: " << pos->str(2) << endl;
        cout << endl;
    }
    return 0;
}

補充:

  1. 同上,模式中也可以沒有分組,這樣也就沒有pos->str(1)其他的了。

  2. 還有一種更高級的捕捉,能夠實現一模一樣的功能,只是對捕獲的結果的組織順序不相同,它將所有與第一個分組匹配的結果,放在一個容器內。

    運行如下代碼,感受一下:

    #include <bits/stdc++.h>
    using namespace std;
    int main()
    {
        string str = "boo@gmail.com  boqian@hotmail.com bo_qian@163.com";
        regex e("([[:w:]]+)@([[:w:]]+)\.com");
        sregex_token_iterator pos(str.cbegin(), str.cend(), e, 1);// 表示第一個分組,再換成0、2、-1感受一下
        sregex_token_iterator end;
        
        for(; pos!=end; ++pos)
        {
            cout << "Matched: " << pos->str() << endl;
            cout << endl;
        }
        return 0;
    }
    

捕捉的同時生成新串

regex_replace方法先完成捕捉,得到類似於:

大匹配1:分組1、分組2
大匹配2:分組1、分組2
大匹配3:分組1、分組2
...

所以如下代碼:

#include <bits/stdc++.h>
using namespace std;
int main()
{
    string str = "boo@gmail.com  boqian@hotmail.com bo_qian@163.com";
    regex e("([[:w:]]+)@([[:w:]]+)\.com");
    cout << regex_replace(str, e, "$1 is on $2\n", regex_constants::format_no_copy); // 除了捕捉到的組以外,其他的東西均舍棄
    return 0;
}

運行結果:

補充:

  1. regex_replace除了regex_constants::format_no_copy以外,還有其他flag,如regex_constants::format_first_only表示只取“大匹配1”,而忽略其他。
  2. flag和flag之間通過|相連接。
  3. regex_matchregex_search也有自己的flag,如用於忽略英文字母大小寫的regex_constants::icase

參考資料

https://www.bilibili.com/video/av7701532/?p=12
https://zh.cppreference.com/w/cpp/regex


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM