C++正则表达式小结

自2011年,C++ 11也将regex列为新标准的一部分。不仅如此,它还支持了6种不同的正则表达式的语法,分别是:ECMASCRIPT、basic、extended、awk、grep和egrep。其中ECMASCRIPT是默认的语法。而正则表达式我在这三年经常接触,往往是学了就忘。在这段时间的C++刷题经验上来看,掌握正则表达式有时候可以极大的节省你做题的时间,比如 这个题
这是一篇更偏应用的文章,我们这里就直接上一些例子就行。
第一类就是比较常见的单次匹配问题了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//写一个匹配函数,()可以分隔语义 ,\\进行转义
bool IsIP(string Str) {
//regex e("\\d+\\.\\d+\\.\\d+\\.\\d"); //IP地址匹配
//regex e("[a-g]"); //区间匹配
//regex e("[a-g]*"); //*号与区间匹配联合使用
//regex e("\\w{4,10}"); //表示4-10个字符 指定个数
//regex e("ada(e|b)"); //表示或者
//regex e("^[a-z]+\\d{1,3}"); //属于小写字母开头包含几个数字而[^a-z] 不属于a-z
//regex e("ab[^cd]*"); // [^...] 任意不在方括号中的字符
//regex e("[a-zA-Z][a-zA-Z0-9_]{4,15}");
//BUAA机考题
//regex e("a[abc]b[abc]c",regex::icase); //忽略大小写
//regex e("\\w*"); //注意是否转义有很大区别
//regex e("abc.", regex::icase); // . 表示除了换行符之外任意字符
//regex e("abc?"); // ? 0个或者1个前面的字符
//regex e("abc|de[fg]"); // | 或者
//regex e("(abc)de+\\1"); // \1 第1个子串
//regex e("(ab)c(de+)\\2\\1");
//regex e("\\w+@[[:w:]]+\\.com"); // [[:w:]] :字母,数字,下划线
regex e("^abc.+$"); // $ 行尾 ^顶格
bool b = regex_match(Str, e);
return b;
}
}

第二类问题和子串提取,多重匹配有关。
上面说了,()可以分隔语义,也可以分出子串1,子串2,子串3。那么,我们也可以通过smatch把这几个子串重定向出来。
这是一些知识总结和实例:

  • smatch string类型的详细的匹配Detailed match in string
  • smatch m; 其实是一个类似vector的数组,但是不是直接按照string存的
  • m[0].str() 整个匹配的字符串 (同m.str(), m.str(0))
  • m[1].str() 第1个子串(同m.str(1)),所以需要一个.str()方法转为string
  • m[2].str() 第2个子串
  • m.prefix() 所有匹配字符串之前的部分
  • m.suffix() 所有匹配字符串之后的部分
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    int main() {
    string str;

    while (true) {
    cin >> str;
    smatch m; // typedef std::match_results<string>

    regex e("([[:w:]]+)@([[:w:]]+)\\.com");

    bool found = regex_search(str, m, e); //只返回第一个匹配

    cout << "m.size() " << m.size() << endl; //size()==子匹配个数+1
    for (int n = 0; n< m.size(); n++) {
    cout << "m[" << n << "]: str()=" << m[n].str() << endl;
    }
    cout << "m.prefix().str(): " << m.prefix().str() << endl;
    cout << "m.suffix().str(): " << m.suffix().str() << endl;
    }
    }

在上面的例子中,展示了如何提取匹配出的第一个子串,第二个子串,但有时候我们的野心不止于此,我们还希望能够对多个match的串,分别算出他们的子串,这怎么办呢?这时候,就可以用一个很STL style的sregex迭代器来处理了。Show you my code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int main() {
cout << "Hi" << endl;
string str = "boq@yahoo.com, boqian@gmail.com; bo@hotmail.com";
regex e("\\w+@\\w+\\.com");
sregex_iterator pos(str.cbegin(), str.cend(), e);
sregex_iterator end; // 默认构造定义了past-the-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;
}
cout << "=============================\n\n";
}

第三类问题和匹配替换有关。
regex_replace()算法要求输入一个正则表达式,以及一个用于替换匹配子字符串的格式化字符串。这个格式化字符串可以通过转义序列引用匹配子字符串中的部分内容。

  • $n 匹配第n个捕捉组的字符串。例如\$l表示第一个捕捉组,\$2表示第二个,依此类推
  • $& 匹配整个正则表达式的字符串,等同于\$0
  • $` 在源字符串中,在匹配正则表达式的子字符串左侧的部分
  • $’ 在源字符串中,在匹配正则表达式的子字符串右侧的部分
  • $$ 美元符号
    看一个简单例子:
    1
    2
    3
    4
    5
    int main() {
    string str = "11sigalhu22sigalhu33",str1;
    str1 = regex_replace(str,regex("s(igal)h(u)"),"SS$1HH$2");
    cout<<str1<<endl;
    //应该输出11SSigalHHu22SSigalHHu33