C++ 正则表达式示例
一、正则表达式类对象
C++标准库中提供了对正则表达式的支持,一下是常用的使用方法:
1. regex 类:
定义包含正则表达式的对象,如regex rx("a(b?)c");
2. cmath 类:
定义保存匹配结果的对象;
当待搜索的字符串是char类型时,使用此类对象;
3. smath 类:
定义保存匹配结果的对象;
当待搜索的字符串是string类型时,使用此类对象;
4. 常用正则匹配函数:
4.1 bool regex_match(...)
判断是否准确匹配整个目标字符串,是目标字符串和正则表达式要完全匹配时才返回TRUE;
如"abc"和"ab*c" 完全匹配,但是如果是"abcd"和"ab*c",虽然只有部分匹配(abc)但是返回是false;
4.2 bool regex_search(...)
在目标字符串中搜索一个匹配正则的字符串,如果搜索到了则返回true,否则返回false;
4.3 regex_replace(...)
用指定的字符串替换匹配到的字符串,默认是替换所有目标字符串中匹配到的字符串,加了format_first_only标志表示只替换第一次匹配到的字符串;
二、使用示例
1 #include <iostream> 2 #include <sstream> 3 #include <fstream> 4 #include <string> 5 #include <regex> 6 7 using namespace std; 8 9 int main(int argc, char * argv[]) 10 { 11 const char * first = "abc"; //待匹配字符串 12 const char * last = first + strlen(first); 13 cmatch narrowMatch; //char *类型的对象来匹配保存结果 14 regex rx("ab*c"); //定义包含正则表达式的对象 15 16 // 注意:regex_match是目标字符串和正则表达式要完全匹配时才返回true. 17 // 如"abc"和"ab*c"完全匹配返回true,但是如果是"abcd"和"ab*c",虽然只有 18 // 部分匹配(abc)但是返回是false 19 // regex_match有多个重载函数,可以只有三个参数,不保存结果. 20 // 也可以有四个参数,第三个参数用来保存结果,一般情况下使用三个参数的就可以了 21 bool found = regex_match( 22 first, //待匹配开始位置 23 last, //待匹配的结束位置 24 narrowMatch, // 保存结果 25 rx //正则表达式 26 ); 27 28 cout << found << endl; 29 //cout << narrowMatch << endl; //错误 30 31 string target2("Drizzle"); 32 regex rx2("(D\\w+e)"); 33 smatch result; 34 found = regex_match( 35 target2.cbegin(), //匹配开始 -->迭代器区间开始开始位置 36 target2.cend(), //匹配结束 -->迭代器区间结束位置 37 result, //保存结果 38 rx2 //正则表达式 39 ); 40 cout << found << endl; 41 //cout << result << endl; //错误 42 }
1 #include <iostream> 2 #include <regex> 3 #include <fstream> 4 #include <string> 5 #include <sstream> 6 7 using namespace std; 8 9 int main(void) 10 { 11 const char * first = "abcd"; 12 const char * last = first + strlen(first); 13 cmatch mr; //保存匹配结果, 可打印 14 regex rx("abc"); 15 std::regex_constants::match_flag_type fl = std::regex_constants::match_default; //匹配标志 16 17 // 给定目标字符串的起始和结束位置 18 // 完全和正则表达式匹配,不同于regex_match() 19 // 可打印正确匹配的结果,不同于regex_match() 20 bool search1 = regex_search(first, first+1, rx, fl); 21 cout << "search1: " << search1 << endl; 22 23 bool search2 = regex_search(first, last, mr, rx); 24 cout << "search2: " << search2 << "; mr: " << mr.str() << endl; 25 26 // 给定待匹配的字符串(char类或string类) 27 bool search3 = regex_search("a", //待匹配字符串 28 rx); 29 cout << "search3: " << search3 << endl; 30 31 bool search4 = regex_search("xabcd", mr, rx); 32 cout << "search4: " << search4 << "; mr: " << mr.str() << endl; 33 34 bool search5 = regex_search(string("a"), //待匹配对象,string类 35 rx); 36 cout << "search5: " << search5 << endl; 37 38 string st("abcabc"); 39 smatch mr2; //保存匹配结果,可打印 40 bool search6 = regex_search(st, mr2, rx); 41 cout << "search6: " << search6 << "; mr2: " << mr2.str() << endl; 42 43 return 0; 44 }
1 #include <iostream> 2 #include <sstream> 3 #include <string> 4 #include <regex> 5 6 using namespace std; 7 8 int main(void) 9 { 10 char buf[20]; 11 const char * first = "axayaz"; 12 const char * last = first + strlen(first); 13 14 regex rx("a"); 15 string fmt("A"); 16 regex_constants::match_flag_type fonly = regex_constants::format_first_only; 17 18 // 默认是替换所有目标字符串总匹配到的字符串 19 // format_first_only标志表示只替换第一次匹配到的字符串 20 // 输出替换后的字符串 21 *regex_replace( 22 &buf[0], //被更改字符串的迭代器 23 first, 24 last, 25 rx, 26 fmt //要替换的字符串 27 ) = '\0'; 28 cout << "replacement1: " << &buf[0] << endl; 29 30 *regex_replace( 31 &buf[0], 32 first, 33 last, 34 rx, 35 fmt, 36 fonly //替换标志,在此表示只替换第一次匹配到的字符串 37 ) = '\0'; 38 cout << "replacement2: " << &buf[0] << endl; 39 40 string str("adaeaf"); 41 string replacement3 = regex_replace( 42 str, 43 rx, 44 fmt 45 ); 46 cout << "replacement3: " << replacement3 << endl; 47 48 string replacement4 = regex_replace( 49 str, 50 rx, 51 fmt, 52 fonly 53 ); 54 cout << "replacement4: " << replacement4 << endl; 55 56 return 0; 57 }
1 #include <iostream> 2 #include <regex> 3 #include <fstream> 4 #include <string> 5 #include <sstream> 6 7 using namespace std; 8 9 int main(void) 10 { 11 const char * first = "abcd"; 12 const char * last = first + strlen(first); 13 cmatch mr; //保存匹配结果, 可打印 14 regex rx("abc"); 15 std::regex_constants::match_flag_type fl = std::regex_constants::match_default; //匹配标志 16 17 // 给定目标字符串的起始和结束位置 18 // 完全和正则表达式匹配,不与待搜索字符串同长度,不同于regex_match() 19 // 可打印正确匹配的结果,不同于regex_match() 20 bool search1 = regex_search(first, first+1, rx, fl); 21 cout << "search1: " << search1 << endl; 22 23 bool search2 = regex_search(first, last, mr, rx); 24 cout << "search2: " << search2 << "; mr: " << mr.str() << endl; 25 26 // 给定待匹配的字符串(char类或string类) 27 bool search3 = regex_search("a", //待匹配字符串 28 rx); 29 cout << "search3: " << search3 << endl; 30 31 bool search4 = regex_search("xabcd", mr, rx); 32 cout << "search4: " << search4 << "; mr: " << mr.str() << endl; 33 34 bool search5 = regex_search(string("a"), //待匹配对象,string类 35 rx); 36 cout << "search5: " << search5 << endl; 37 38 string st("abcabc"); 39 smatch mr2; //保存匹配结果,可打印 40 bool search6 = regex_search(st, mr2, rx); 41 cout << "search6: " << search6 << "; mr2: " << mr2.str() << endl; 42 43 ///////////////////////////////4.regex_search(...)循环遍历字符串示例///////////////////////// 44 /// --------->找到目标字符串中所有匹配的子串 45 /// ####----->此示例中找到s串中所有以subj开头的单词,并打印出来 46 /////////////////////////////////////////////////////////////////////////////////////////// 47 string s("this subject has a subjmarine as a subjsequence subjmite"); 48 smatch m; 49 regex e("\\b(subj)([^ ]*)/"); 50 while (regex_search(s, m, e)) 51 { 52 cout << m.str() << endl; 53 s = m.suffix().str(); 54 } 55 56 return 0; 57 }
三、特殊字符及语法表达式
参考:
http://deerchao.net/tutorials/regex/regex.htm
http://www.regexlab.com/zh/regref.htm
http://www.cplusplus.com/reference/regex/ECMAScript/
正则表达式(regular expression)就是用一个“字符串”来描述一个特征,然后去验证另一个“字符串”是否符合这个特征。比如 表达式“ab+” 描述的特征是“一个 ‘a’ 和 任意个 ‘b’ ”,那么 ‘ab’, ‘abb’, ‘abbbbbbbbbb’ 都符合这个特征。
正则表达式可以用来:
- 验证字符串是否符合指定特征,比如验证是否是合法的邮件地址。
- 用来查找字符串,从一个长的文本中查找符合指定特征的字符串,比查找固定字符串更加灵活方便。
- 用来替换,比普通的替换更强大。
从c++ 11开始,标准c++库在 里面提供了正则表达式的支持。默认的,c++ regex正则表达式使用的是ECMAScript语法。
ECMAScript语法
普通字符
字母、数字、汉字、下划线、以及后边没有提到的特殊模式字符,都是”普通字符”。表达式中的普通字符,在匹配一个字符串的时候,匹配与之相同的一个字符。
举例1:表达式 “c”,在匹配字符串 “abcde” 时,匹配结果是:成功;匹配到的内容是:”c”;匹配到的位置是:开始于2,结束于3。(注:下标从0开始还是从1开始,因当前编程语言的不同而可能不同)
举例2:表达式 “bcd”,在匹配字符串 “abcde” 时,匹配结果是:成功;匹配到的内容是:”bcd”;匹配到的位置是:开始于1,结束于4。
特殊模式字符
特殊模式字符是在正则表达式中有特殊含义的字符,比如代表某些无法用字符串表达的字符或者代表一类字符。
表达式 | 可匹配 |
r | 回车 |
n | 换行 |
f | 换页 |
t | 水平制表符 |
v | 数值制表符 |
. | 除了行结束符(LF CR LS PS)外所有字符(LF换行,CR回车,LS行分割,PS段落分割) |
d | 0~9 中的任意一个数字 |
D | d的反面 |
w | 任意一个字母或数字或下划线,也就是 A~Z,a~z,0~9,_ 中任意一个 |
W | w的反面 |
s | 包括空格、制表符、换页符等空白字符的其中任意一个 |
S | s的反面 |
字符类
使用方括号 [ ] 包含一系列字符,能够匹配其中任意一个字符。用 [^ ] 包含一系列字符,则能够匹配其中字符之外的任意一个字符。同样的道理,虽然可以匹配其中任意一个,但是只能是一个,不是多个。
表达式 | 可匹配 |
[ab5@] | 匹配 “a” 或 “b” 或 “5” 或 “@” |
[^abc] | 匹配 “a”,”b”,”c” 之外的任意一个字符 |
[f-k] | 匹配 “f”~”k” 之间的任意一个字母 |
[^A-F0-3] | 匹配 “A”~”F”,”0″~”3″ 之外的任意一个字符 |
举例1:表达式 “[bcd][bcd]” 匹配 “abc123” 时,匹配的结果是:成功;匹配到的内容是:”bc”;匹配到的位置是:开始于1,结束于3。
举例2:表达式 “[^abc]” 匹配 “abc123” 时,匹配的结果是:成功;匹配到的内容是:”1″;匹配到的位置是:开始于3,结束于4。
量词
字符 | 次数 |
* | 0到无限次 |
+ | 1到无限次 |
? | 0到1次 |
{int} | int次 |
{int,} | int到无限次 |
{min,max} | 大于等于min次,小于等于max次 |
举例1:表达式 “d+.?d*” 在匹配 “It costs $12.5” 时,匹配的结果是:成功;匹配到的内容是:”12.5″;匹配到的位置是:开始于10,结束于14。
举例2:表达式 “go{2,8}gle” 在匹配 “Ads by goooooogle” 时,匹配的结果是:成功;匹配到的内容是:”goooooogle”;匹配到的位置是:开始于7,结束于17。
默认的,这些量词都是贪婪的,就是他们会尽可能的去匹配最多的字符。在量词后面加上?,这些行为可以改成懒惰的。
比如:”(a+).*”去匹配”aardvark”,得到结果是”aa”。
“(a+?).*”去匹配”aardvark”,得到结果是”a”。
贪婪与懒惰
当正则表达式中包含能接受重复的限定符时,通常的行为是(在使整个表达式能得到匹配的前提下)匹配尽可能多的字符。以这个表达式为例:a.*b,它将会匹配最长的以a开始,以b结束的字符串。如果用它来搜索aabab的话,它会匹配整个字符串aabab。这被称为贪婪匹配。
有时,我们更需要懒惰匹配,也就是匹配尽可能少的字符。前面给出的限定符都可以被转化为懒惰匹配模式,只要在它后面加上一个问号?。这样.*?就意味着匹配任意数量的重复,但是在能使整个匹配成功的前提下使用最少的重复。现在看看懒惰版的例子吧:
a.*?b匹配最短的,以a开始,以b结束的字符串。如果把它应用于aabab的话,它会匹配aab(第一到第三个字符)和ab(第四到第五个字符)。
懒惰限定符
表达式 | 匹配 |
*? | 重复任意次,但尽可能的少 |
+? | 重复1次或更多次,但尽可能少重复 |
?? | 重复0次或1次,但尽可能少重复 |
{n,m}? | 重复n到m次,但尽可能少重复 |
{n,}? | 重复n次以上,但尽可能少重复 |
分组
分组可以使量词作用在几个字符上。有两种分组:
字符 | 描述 | 效果 |
(subpattern) | Group | 创造反向引用 |
(?:subpattern) | Passive Group | 不创造反向引用 |
当分组创建了一个反向引用,分组内的子表达式会存储成一个子匹配。每个子匹配的都是以左括号出现次序为数字的索引。第一个子匹配是1,第二个是2,一次类推。这些子匹配可以用在后续的正则表达式中。
选择
模式中存在不同的可选部分。
举例5:表达式 “Tom|Jack” 在匹配字符串 “I’m Tom, he is Jack” 时,匹配结果是:成功;匹配到的内容是:”Tom”;匹配到的位置是:开始于4,结束于7。匹配下一个时,匹配结果是:成功;匹配到的内容是:”Jack”;匹配到的位置时:开始于15,结束于19。
举例6:表达式 “(gos*)+” 在匹配 “Let’s go go go!” 时,匹配结果是:成功;匹配到内容是:”go go go”;匹配到的位置是:开始于6,结束于14。
举例7:表达式 “¥(d+.?d*)” 在匹配 “$10.9,¥20.5” 时,匹配的结果是:成功;匹配到的内容是:”¥20.5″;匹配到的位置是:开始于6,结束于10。单独获取括号范围匹配到的内容是:”20.5″。
反向引用
表达式在匹配时,表达式引擎会将小括号 “( )” 包含的表达式所匹配到的字符串记录下来。在获取匹配结果的时候,小括号包含的表达式所匹配到的字符串可以单独获取。在实际应用场合中,当用某种边界来查找,而所要获取的内容又不包含边界时,必须使用小括号来指定所要的范围。
其实,”小括号包含的表达式所匹配到的字符串” 不仅是在匹配结束后才可以使用,在匹配过程中也可以使用。表达式后边的部分,可以引用前面 “括号内的子匹配已经匹配到的字符串”。引用方法是 “” 加上一个数字。”1″ 引用第1对括号内匹配到的字符串,”2″ 引用第2对括号内匹配到的字符串……以此类推,如果一对括号内包含另一对括号,则外层的括号先排序号。换句话说,哪一对的左括号 “(” 在前,那这一对就先排序号。
举例1:表达式 “(‘|”)(.*?)(1)” 在匹配 ” ‘Hello’, “World” ” 时,匹配结果是:成功;匹配到的内容是:” ‘Hello’ “。再次匹配下一个时,可以匹配到 ” “World” “。
举例2:表达式 “(w)1{4,}” 在匹配 “aa bbbb abcdefg ccccc 111121111 999999999” 时,匹配结果是:成功;匹配到的内容是 “ccccc”。再次匹配下一个时,将得到 999999999。这个表达式要求 “w” 范围的字符至少重复5次,点击测试 注意与 “w{5,}” 之间的区别。
举例3:表达式 “<(w+)s*(w+(=(‘|”).*?4)?s*)*>.*?</1>” 在匹配 ”
” 时,匹配结果是成功。如果 ” ” 与 ”
” 不配对,则会匹配失败;如果改成其他配对,也可以匹配成功。
断言
断言是判断字符出现位置的条件,它本身不代表任何字符。
字符 | 描述 | 匹配条件 |
^ | 行开始 | 一行的开头或者紧跟行结束符后面 |
$ | 行结束 | 一行的末尾或者紧跟行结束前面 |
b | 字符边界 | 开头和结尾被认为是非字符 |
B | 非字符边界 | 开头和结尾被认为是非字符 |
(?=subpattern) | 正向预搜索匹配 | 正向预搜索匹配 |
(?!subpattern) | 正向预搜索不匹配 | 正向预搜索不匹配 |
格式:”(?=xxxxx)”,在被匹配的字符串中,它对所处的 “缝隙” 或者 “两头” 附加的条件是:所在缝隙的右侧,必须能够匹配上 xxxxx 这部分的表达式。因为它只是在此作为这个缝隙上附加的条件,所以它并不影响后边的表达式去真正匹配这个缝隙之后的字符。这就类似 “b”,本身不匹配任何字符。”b” 只是将所在缝隙之前、之后的字符取来进行了一下判断,不会影响后边的表达式来真正的匹配。
举例1:表达式 “Windows (?=NT|XP)” 在匹配 “Windows 98, Windows NT, Windows 2000” 时,将只匹配 “Windows NT” 中的 “Windows “,其他的 “Windows ” 字样则不被匹配。
格式:”(?!xxxxx)”,所在缝隙的右侧,必须不能匹配 xxxxx 这部分表达式。
举例3:表达式 “((?!bstopb).)+” 在匹配 “fdjka ljfdl stop fjdsla fdj” 时,将从头一直匹配到 “stop” 之前的位置,如果字符串中没有 “stop”,则匹配整个字符串。
举例4:表达式 “do(?!w)” 在匹配字符串 “done, do, dog” 时,只能匹配 “do”。在本条举例中,”do” 后边使用 “(?!w)” 和使用 “b” 效果是一样的。