本文是C++11新特性介绍的第十部分,涉及到正则表达式相关的新特性。
题外话
对regex的完整支持,直到g++4.9才算完善。我使用的系统是Ubuntu14.04,默认g++版本号是4.8.x,所以有一些regex功能无法编译通过。可以通过以下方法安装g++4.9:
1 2 3
| sudo add-apt-repository ppa:ubuntu-toolchain-r/test sudo apt-get update sudo apt=get install gcc-4.9 g++-4.9 c++-4.9
|
安装完成之后,需要在Makefile中强制指定所使用的g++版本。
正则文法
std::regex默认使用是ECMAScript文法,这种文法比较好用,且威力强大,常用符号的意义如下:
符号 |
意义 |
^ |
匹配行的开头 |
$ |
匹配行的结尾 |
. |
匹配任意单个字符 |
[…] |
匹配[]中的任意一个字符 |
(…) |
设定分组 |
\ |
转义字符 |
\d |
匹配数字[0-9] |
\D |
\d 取反 |
\w |
匹配字母[a-z],数字,下划线 |
\W |
\w 取反 |
\s |
匹配空格 |
\S |
\s 取反 |
+ |
前面的元素重复1次或多次 |
* |
前面的元素重复任意次 |
? |
前面的元素重复0次或1次 |
{n} |
前面的元素重复n次 |
{n,} |
前面的元素重复至少n次 |
{n,m} |
前面的元素重复至少n次,至多m次 |
上面列出的这些都是非常常用的符号,靠这些便足以解决绝大多数问题了。
简单用法
一切就绪,先看看如何用C++11中的regex匹配一个电子邮箱地址。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| std::cout<<"test regex simple usage:\n"; std::string email_pattern("(\\w)+(\\.\\w+)*@(\\w)+((\\.\\w+)+)"); try { std::regex email_regex(email_pattern); std::smatch results; std::string test_email_str = "My email is yubo1911@163.com"; if(std::regex_search(test_email_str, results, email_regex)) { std::cout<<results.str()<<std::endl; } } catch(std::regex_error e) { std::cout<<e.what()<<'\t'<<e.code()<<std::endl; } std::cout<<"test regex simple usage done.\n"<<std::endl;
|
C++中的regex默认使用ECMA-262正则表达式规范,这也是众多浏览器所使用的标准。注意到email_pattern中有好多双斜线,这是因为除了regex模块要做一次转义解析外,C++ string也会对字符串做一次转义解析。
regex 选项
在构造regex对象时,可以指定多种标识已实现特定的效果。这里以使用regex::icase达到不区分大小写的匹配为例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| std::cout<<"test regex icase:\n"; try { std::regex cpp_regex("(\\w)+\\.(cpp|hpp)$", std::regex::icase); std::vector<std::string> test_filenames = {"regex.cpp", "iostream.h", "template.CPP", "class.hPP", "Ah, regex.cpp", "regex.cpp Ah"}; for(auto fn : test_filenames) { if(std::regex_match(fn, cpp_regex)) { std::cout<<"cpp file: "<<fn<<'\n'; } } } catch (std::regex_error e) { std::cout<<e.what()<<'\t'<<e.code()<<std::endl; } std::cout<<"test regex icase done.\n"<<std::endl;
|
regex iterator
regex提供了一个迭代器,这个迭代器生成时需要一个所搜寻字符串的范围以及一个regex对象。之后,迭代器在迭代时,会遍历搜寻字符串中的所有匹配位置。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| std::cout<<"test regex iterator usage:\n"; try { std::regex email_regex(email_pattern); std::string test_email_str = "I have three emails, yubo1911@163.com, yubo@gmail.com and guoyubo@gmail.com."; for(std::sregex_iterator it(std::begin(test_email_str), std::end(test_email_str), email_regex), end_it; it != end_it; it++) { std::cout<<it->str()<<std::endl; } } catch(std::regex_error e) { std::cout<<e.what()<<'\t'<<e.code()<<std::endl; } std::cout<<"test regex iterator usage done.\n"<<std::endl;
|
子表达式
regex也支持子表达式,和其他正则表达式一样,使用括号括起来的构成一个子表达式。在匹配结果中,序号0表示整个匹配结果,序号1表示子表达式1的匹配结果……
下面以一个座机电话号码的匹配验证为例,说明子表达式的运用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| std::cout<<"test regex sub_match usage:\n"; std::string phone_pattern = "(\\()?(\\d{3,4})(\$$)?([- ])?(\\d{7,8})"; try { std::regex phone_regex(phone_pattern); std::smatch results; std::vector<std::string> test_phones = {"010-82224567", "(010-83332345", "(020)62334567", "(021) 22346543", "0357-4223456", "0358-465788"}; for(auto fn : test_phones) { if(std::regex_match(fn, results, phone_regex)) { if(results[1].matched) { if(!results[3].matched) continue; if(results[4].matched && results[4].str() == "-") continue; } else { if(results[3].matched) continue; if(!(results[4].matched && results[4].str() == "-")) continue; } std::cout<<results.str()<<std::endl; } } } catch(std::regex_error e) { std::cout<<e.what()<<'\t'<<e.code()<<std::endl; } std::cout<<"test regex sub_match usage done.\n"<<std::endl;
|
replace
regex同样提供了替换功能。将替换功能和子表达式结合起来,可以实现字符串的格式化功能:
1 2 3 4 5 6 7 8 9 10 11 12 13
| std::cout<<"test regex replace usage:\n"; try { std::string format = "$2-$5"; std::regex phone_regex(phone_pattern); std::string ori_phone = "yubo: (020)85776452"; std::cout<<"formated phone: "<<std::regex_replace(ori_phone, phone_regex, format) <<std::endl; } catch(std::regex_error e) { std::cout<<e.what()<<'\t'<<e.code()<<std::endl; } std::cout<<"test regex replace usage done.\n"<<std::endl;
|
其中format字符串中$2和$5就分别表示第二个子表达式和第5个子表达式。
输出
整个测试程序的输出结果如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| test regex simple usage: yubo1911@163.com test regex simple usage done. test regex icase: cpp file: regex.cpp cpp file: template.CPP cpp file: class.hPP test regex icase done. test regex iterator usage: yubo1911@163.com yubo@gmail.com guoyubo@gmail.com test regex iterator usage done. test regex sub_match usage: 010-82224567 (020)62334567 (021) 22346543 0357-4223456 test regex sub_match usage done. test regex replace usage: formated phone: yubo: 020-85776452 test regex replace usage done.
|
总结
1.C++11种提供了regex模块,需要g++-4.9以上才能完整支持。
2.regex默认采用ECMA-262标准,和浏览器中使用的一样。
3.regex提供了查找、匹配、迭代器、子表达式、替换等常用用法。