理解C++11正则表达式(2)
今天有幸(2016/3/19)在上海参加了C++交流会,见到了梦寐已久想见的台湾C++大神老师侯捷,心情十分的激动。侯老师对C++理解的深刻,让人叹为观止。以为他教学的严谨,说话方式娓娓道来,听着非常舒服。末尾附上一张侯老师照片。
我们接着上文介绍C++11的正则表达式。本节将接着上文遗留问题开始展开,并且将结合网上的一些优秀的博客。
正文
C++11 支持六种正则表达式语法:
ECMAScript,
basic(POSIX Basic Regular Expressions),
extended(POSIX Extended Regular Expressions ),
awk(POSIX awk) ,
grep(POSIX grep ),
egrep(POSIX grep –E)。其中ECMAScript最为强大。
首先,我们将介绍正则表达式一些通用的基本类型。
1 basic_regex
//basic_regex: 这是一个包含一个正则表达式的模板类。通常有两种特化方式: a) typedef basic_regex<char> regex; b) typedef basic_regex<wchar_t> wregex;
2 match_results:
这个类包含了与给定正则表达式匹配的序列。当empty()成员返回true或者size()成员返回0,表明没有找到匹配项。
否则,当empty()返回false,size()返回值>=1 表明发生了匹配。
//match_results有如下特化方式: a) typedef match_results<const char*> cmatch; b) typedef match_results<const wchar_t*> wcmatch; c) typedef match_results<string::const_iterator> smatch; d) typedef match_results<wstring::const_iterator> wsmatch;
match[0]:代表整个匹配序列 ; match[1]:代表第一个匹配子序列; match[2]: 代表第二个匹配子序列,以此类推。
特别需要注意的是:正则表达式的编写过程中,需要达到上面match[1][2][3]效果的话,必须将每个match数据通过捕获组(capture group)的方式用括号括起来(.*),例如:
regex reg("<(.*)>(.*)</(\\1)>")
如果你把括号删除,显然是没有上面说的那种效果的。其中\1指的是第一个捕获组,\2 当然就是指代第二个啦。
3 sub_match: 该模板类用来表示与一个已标记的子表达式匹配的序列。这个匹配是通过一个迭代器对来表示的,该迭代器对表明了已匹配的正则表达式的一个范围。可以特化为下面几种情况:
a) typedef sub_match<const char*> csub_match; b) typedef sub_match<const wchar_t*> wcsub_match; c) typedef sub_match<string::const_iterator> ssub_match; d) typedef sub_match<wstring::const_iterator> wssub_match;
4 迭代器介绍:正则表达式迭代器用来遍历这个正则表达式序列,通过一个迭代器区间来表示匹配的区间。
1. regex_iterator: a)typedef regex_iterator<const char*> cregex_iterator; b)typedef regex_iterator<const wchar_t*> wcregex_iterator; c)typedef regex_iterator<string::const_iterator> sregex_iterator; d)typedef regex_iterator<wstring::const_iterator> wsregex_iterator; 2. regex_token_iterator: a) typedef regex_token_iterator<const char*> cregex_token_iterator; b) typedef regex_token_iterator<const wchar_t*> wcregex_token_iterator; c) typedef regex_token_iterator<string::const_iterator> sregex_token_iterator; d) typedef regex_token_iterator<wstring::const_iterator> wsregex_token_iterator;
一些test代码:
string data = "XML tag: <tag-name>the value</tag-name>."; cout << "data: " << data << endl << endl; smatch m; bool found = regex_search(data, m, regex("<(.*)>(.*)</(\\1)>")); cout << "m.empty() " << boolalpha << m.empty() << endl; cout << "m.size() " << m.size() << endl; if (found) { cout << "m.str() " << m.str() << endl; cout << "m.length() " << m.length() << endl; cout << "m.position() " << m.position() << endl; cout << "m.prefix().str() " << m.prefix().str() << endl; cout << "m.suffix().str() " << m.suffix().str() << endl; } for (int i = 0; i < m.size(); i++) { cout << "m[" << i << "].size():" << m[i].str() << endl; cout << "m.str(" << i << "):" << m.str(i) << endl; cout << "m.position(" << i << "):" << m.position(i) << endl; } cout << "match: " << endl; for (auto pos = m.begin(); pos != m.end(); pos++) { cout << " " << *pos << " "; cout << "(length" << pos->length() << ")" << endl; } //第二个例子 data = "<person>\n" " <first>Nico</first>\n" " <last>Josuttis</last>\n" "</person>\n"; auto pos = data.cbegin(); auto end = data.cend(); regex reg("<(.*)>(.*)</(\\1)>"); for (;regex_search(pos, end, m, reg); pos = m.suffix().first) { cout << "match: " << m.str() << endl; cout << "tag: " << m.str(1) << endl; cout << "value: " << m.str(2) << endl; }
我们要查找的字符串是这样的格式:
另外,第二个例子中将字符串添加换行符,如果没有添加,将无法辨别<first><last>,最后只能找到<person>。
Regex Iterator: 采用迭代器的方式进行访问或通过for_each()
sregex_iterator pos(data.cbegin(),data.cend(),reg); sregex_iterator end; for(; pos!=end; ++pos) { cout << "match " << pos -> str() << endl; cout << "tag:" << pos -> str(1) << endl; cout << "value:" << pos -> str(2) << endl; } for_each(beg,end,[](const smatch &m){ cout << "match " << pos -> str() << endl; cout << "tag:" << pos -> str(1) << endl; cout << "value:" << pos -> str(2) << endl; });
Regex Token Iterator
1、regex reg("<(.*)>(.*)</(\\1)>");
sregex_token_iterator pos(data.cbegin(),data.cend(),reg,{0,2});//0代表了获取整个match,2代表获取第二个group;-1代表了想知道所有子序列。
2、regex sep("[[:space:]]*[;,.][[:space:]]*");//以;.,分割。
sregex_token_iterator pos(data.cbegin(),data.cend(),sep,-1);//得到所有子序列。(具体用法请参考c++11标准库,有详细用法和介绍)
小结:
前面2节主要介绍两个函数(regex_search regex_match),但是这两个函数不能定义出在那个位置,我们引入了迭代器、group的概念。下一节主要介绍替换、regex常量以开头说的6种正则表达式语法,最后想通过几个简单的例子练习一下。其实标准库里面说的很清楚,如果不明白的话,建议去买一本侯捷翻译的c++11标准库。