C++11的正则表达式基本用法

1. 介绍

正则表达式(Regular Expression,常简写为regex、regexp或RE)。正则表达式通常被用来检索、替换那些符合某个模式(规则)的文本。C++11开始支持正则表达式。
正则表达式非常强大,具体的实现算法有差异,所以会有多种实现方式。C++11支持6种正则表达式引擎。ECMAScript 是其中支持最多元素的引擎,也是regex默认支持的引擎。

  • ECMAScript
  • basic(POSIX Basic Regular Expressions)
  • extended(POSIX Extended Regular Expressions )
  • awk(POSIX awk)
  • grep(POSIX grep )
  • egrep(POSIX grep –E)

2. 正则表达式语法

正则表达式主要两部分构成,特殊字符和普通字符。

  1. “.”: 匹配除"\n"之外的任何单个字符,若要匹配包括"\n"在内的任意字符,需使用诸如"[\s\S]"之类的模式;

  2. “^”:匹配输入字符串的开始位置,不匹配任何字符,要匹配”^”字符本身,需使用”^”;

  3. “$”:匹配输入字符串结尾的位置,不匹配任何字符,要匹配”$”字符本身,需使用”$”;

  4. “*”: 零次或多次匹配前面的字符或子表达式,”*”等效于”{0,}”,如”^*b”可以匹配”b”、”^b”、”^^b”、…;

  5. “+”: 一次或多次匹配前面的字符或子表达式,等效于”{1,}”,如”a+b”可以匹配”ab”、”aab”、”aaab”、…;

  6. “?”: 零次或一次匹配前面的字符或子表达式,等效于”{0,1}”,如”a[cd]?”可以匹配”a”、”ac”、”ad”; 当此字符紧随任何其他限定符”*”、”+”、”?”、”{n}”、”{n,}”、”{n,m}”之后时,匹配模式是"非贪心的"。"非贪心的"模式匹配搜索到的、尽可能短的字符串,而默认的"贪心的"模式匹配搜索到的、尽可能长的字符串。如,在字符串"oooo"中,"o+?“只匹配单个"o”,而"o+“匹配所有"o”;

  7. “|”:将两个匹配条件进行逻辑"或"(Or)运算,如正则表达式”(him|her)”匹配"itbelongs to him"和"it belongs to her",但是不能匹配"itbelongs to them.";

  8. “\”: 将下一字符标记为特殊字符、文本、反向引用或八进制转义符,如,”n”匹配字符”n”,”\n”匹配换行符,序列”\”匹配”\”,”(“匹配”(“;

  9. “\w”:匹配字母或数字或下划线,任意一个字母或数字或下划线,即A~Z,a~z,0~9,_中任意一个;

  10. “\W”:匹配任意不是字母、数字、下划线的字符;

  11. “\s”:匹配任意的空白符,包括空格、制表符、换页符等空白字符的其中任意一个,与”[ \f\n\r\t\v]”等效;

  12. “\S”:匹配任意不是空白符的字符,与”[^\f\n\r\t\v]”等效;

  13. “\d”:匹配数字,任意一个数字,0~9中的任意一个,等效于”[0-9]”;

  14. “\D”:匹配任意非数字的字符,等效于”[^0-9]”;

  15. “\b”: 匹配一个字边界,即字与空格间的位置,也就是单词和空格之间的位置,不匹配任何字符,如,“er\b"匹配"never"中的"er”,但不匹配"verb"中的"er";

  16. “\B”: 非字边界匹配,“er\B"匹配"verb"中的"er”,但不匹配"never"中的"er";

  17. “\f”:匹配一个换页符,等价于”\x0c”和”\cL”;

  18. “\n”:匹配一个换行符,等价于”\x0a”和”\cJ”;

  19. “\r”:匹配一个回车符,等价于”\x0d”和”\cM”;

  20. “\t”:匹配一个制表符,等价于”\x09”和”\cI”;

  21. “\v”:匹配一个垂直制表符,等价于”\x0b”和”\cK”;

  22. “\cx”:匹配”x”指示的控制字符,如,\cM匹配Control-M或回车符,”x”的值必须在”A-Z”或”a-z”之间,如果不是这样,则假定c就是"c"字符本身;

  23. “{n}”:”n”是非负整数,正好匹配n次,如,"o{2}"与"Bob"中的"o"不匹配,但与"food"中的两个"o"匹配;

  24. “{n,}”:”n”是非负整数,至少匹配n次,如,"o{2,}“不匹配"Bob"中的"o”,而匹配"foooood"中的所有”o”,"o{1,}“等效于"o+”,"o{0,}“等效于"o*”;

  25. “{n,m}”:”n”和”m”是非负整数,其中n<=m,匹配至少n次,至多m次,如,"o{1,3}"匹配"fooooood"中的头三个o,'o{0,1}‘等效于’o?’,注意,不能将空格插入逗号和数字之间;如”ba{1,3}”可以匹配”ba”或”baa”或”baaa”;

  26. “x|y”:匹配”x”或”y”,如,”z|food”匹配"z"或"food";”(z|f)ood”匹配"zood"或"food";

  27. “[xyz]”:字符集,匹配包含的任一字符,如,"[abc]“匹配"plain"中的"a”;

  28. “[^xyz]””:反向字符集,匹配未包含的任何字符,匹配除了”xyz”以外的任意字符,如,"[^abc]“匹配"plain"中的"p”;

  29. “[a-z]”:字符范围,匹配指定范围内的任何字符,如,"[a-z]"匹配"a"到"z"范围内的任何小写字母;

  30. “[^a-z]”:反向范围字符,匹配不在指定的范围内的任何字符,如,"[^a-z]"匹配任何不在"a"到"z"范围内的任何字符;

  31. “( )”:将”(“和”)”之间的表达式定义为”组”group,并且将匹配这个表达式的字符保存到一个临时区域,一个正则表达式中最多可以保存9个,它们可以用”\1”到”\9”的符号来引用;

  32. “(pattern)”:匹配pattern并捕获该匹配的子表达式,可以使用$0…$9属性从结果”匹配”集合中检索捕获的匹配;"$0"表示整个匹配,"$1"表示第1个子表达式的匹配结果。当只有一个子表达式时两者可能是相同。

  33. “(?:pattern)”:匹配pattern但不捕获该匹配的子表达式,即它是一个非捕获匹配,不存储供以后使用的匹配,这对于用”or”字符” (|)”组合模式部件的情况很有用, 如,”industr(?:y|ies)”是比”industry|industries”更简略的表达式;

  34. “(?=pattern)”: 非获取匹配,正向肯定预查,在任何匹配pattern的字符串开始处匹配查找字符串,该匹配不需要获取供以后使用。如,“Windows(?=95|98|NT|2000)“能匹配"Windows2000"中的"Windows”,但不能匹配"Windows3.1"中的"Windows”。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始;

  35. “(?!pattern)”: 非获取匹配,正向否定预查,在任何不匹配pattern的字符串开始处匹配查找字符串,该匹配不需要获取供以后使用。如"Windows(?!95|98|NT|2000)“能匹配"Windows3.1"中的"Windows”,但不能匹配"Windows2000"中的"Windows";

3. 正则表达式的使用

  • 目标序列(Target sequence)。为模式而搜索到的字符序列。这可以是二个迭代器所指定的范围、空终止字符串或一个 std::string 。
  • 模式(Pattern)。这是正则表达式自身。它确定构成匹配者。它是从带特定语法的字符串构成的 std::basic_regex 类型对象。受支持的语法变体的描述见 syntax_option_type 。
  • 匹配的数组(Matched array)。关于匹配的信息可作为 std::match_results 类型对象获取。
  • 替换字符串(Replacement string)。这是确定如何替换匹配的字符串,受支持的语法变体的描述不同而不同。

3.1 基本类

为了支持宽字符和窄字符,所以正则表达式的类基本上是通过类模板来实现的。

typedef basic_regex<char> regex;					  // 正则表达式对象 
typedef basic_regex<wchar_t> wregex;
typedef match_results<const char *> cmatch;			  // 标识一个正则表达式匹配,包含所有子表达式匹配(字符指针) 
typedef match_results<const wchar_t *> wcmatch;
typedef match_results<string::const_iterator> smatch; // 标识一个正则表达式匹配,包含所有子表达式匹配(字符串) 
typedef match_results<wstring::const_iterator> wsmatch;
typedef sub_match<const char *> csub_match;		      // 标识子表达式所匹配的字符序列 
typedef sub_match<const wchar_t *> wcsub_match;

3.2. 算法

算法将封装于 regex 的正则表达式应用到字符的目标序列,算法主要是由函数模板来实现的。

  • regex_match,试图匹配正则表达式到整个字符序列 。
  • regex_search,试图匹配正则表达式到字符序列的任何部分 。
  • regex_replace,以格式化的替换文本来替换正则表达式匹配的出现位置 。

3.3. 迭代器

迭代器用于遍历在序列中找到的匹配正则表达式的整个集合。

  • regex_iterator,在字符序列中通过所有正则表达式匹配迭代 。
  • regex_token_iterator,通过在给定的字符串中所有正则表达式匹配中的指定子表达式,或通过不匹配的子串迭代 。

3.4 异常

regex_error定义一个对象抛出来自正则表达式库 的异常。

4. 示例

以下代码使用VS2010进行编译测试。

4.1. regex_match

#include "stdafx.h"
#include <regex> 	// Must be included
#include <string>
#include <iostream>
using namespace std;


int _tmain(int argc, _TCHAR* argv[])
{
	// .通配符,几个代表几个字符
  	std::string strSrc = "Simple";
  	std::regex rePattern1("Simpl.");
  	cout<<boolalpha<<std::regex_match(strSrc, rePattern1)<<endl;  // true
  	std::regex rePattern2("Simp..");
  	cout<<boolalpha<<std::regex_match(strSrc, rePattern2)<<endl;  // true

  	// ^在regex_match中的作用无法体现,因为regex_match就要求必须从开始匹配
  	std::regex rePattern3(".impl.");
  	cout<<boolalpha<<std::regex_match(strSrc, rePattern3)<<endl; // true
  	std::regex rePattern4("^.impl.");
  	cout<<boolalpha<<std::regex_match(strSrc, rePattern4)<<endl; // true
  
  	// regex_match可以用作单纯的匹配判断,也可以匹配获得具体的结果
  	// 匹配的结果存放在smatch中.smatch中0索引存放完整匹配的结果,1存放第1个()匹配的结果
  	// 从smatch中取元素,既可以用[],也可以用str(idx)
  	std::smatch matchRes1;
  	strSrc = "foot.txt";
  	std::regex rePattern5("(foot)\\.txt"); // \\.使用转义字符
  	cout<<boolalpha<<std::regex_match(strSrc, matchRes1, rePattern5)<<endl; // true
  	cout<<matchRes1[0]<<":"<<matchRes1[1]<<endl; // foot.txt:foot
  	std::regex rePattern6("(....)\\.txt"); // \\.使用转义字符
  	cout<<boolalpha<<std::regex_match(strSrc, matchRes1, rePattern6)<<endl; // true
  	cout<<matchRes1[0]<<":"<<matchRes1[1]<<endl; // foot.txt:foot
  	cout<<matchRes1[0]<<":"<<matchRes1.str(1)<<endl; // foot.txt:foot
  	std::ssub_match ssMatch = matchRes1[1];
  	// ssMatch支持强制转换成string类型,和ssMatch.str()等价
  	cout<<matchRes1[0]<<":"<<ssMatch<<endl; // foot.txt:foot
  	cout<<matchRes1[0]<<":"<<ssMatch.str()<<endl; // foot.txt:foot
  
  	// *表示匹配前面的o,0或多个均可。即strSrc为ft.txt,fot.txt,foot.txt均匹配成功
  	std::regex rePattern7("fo*t\\.txt"); // \\.使用转义字符
  	cout<<boolalpha<<std::regex_match(strSrc, matchRes1, rePattern7)<<endl; // true
  
  	// +表示匹配前面的o,1或多个均可。即strSrc为fot.txt,foot.txt均匹配成功,ft.txt匹配不成功
  	std::regex rePattern8("fo+t\\.txt"); // \\.使用转义字符
  	cout<<boolalpha<<std::regex_match(strSrc, matchRes1, rePattern8)<<endl; // true
  
  	// ?表示匹配前面的o,0或1个均可。即strSrc为ft.txt,fot.txt均匹配成功,foot.txt匹配不成功
  	std::regex rePattern9("fo?t\\.(txt)"); // \\.使用转义字符
  	cout<<boolalpha<<std::regex_match(strSrc, matchRes1, rePattern9)<<endl; // false
  
  	// 下面有两个括号,即对应两个匹配元素[1],[2]
  	std::regex rePattern10("(foo?t)\\.(txt)"); // \\.使用转义字符
  	cout<<boolalpha<<std::regex_match(strSrc, matchRes1, rePattern10)<<endl; // true
  	cout<<matchRes1[0]<<":"<<matchRes1.str(1)<<":"<<matchRes1[2]<<endl; // foot.txt:foot:txt
  
  	// (ab)是子表达式,(ab)+表示匹配1个或多个ab.
  	strSrc = "abab.txt";
  	std::regex rePattern11("(ab)+\\.(txt)"); // 可匹配ab.txt,abab.txt,ababab.txt等
  	cout<<boolalpha<<std::regex_match(strSrc, matchRes1, rePattern11)<<endl; // true
  	cout<<matchRes1[0]<<":"<<matchRes1.str(1)<<":"<<matchRes1[2]<<endl;      // abab.txt:ab:txt
  
  	// (ab|cd)是子表达式,(ab|cd)+表示匹配1个或多个ab或cd.
  	strSrc = "abcdab.txt";
  	std::regex rePattern12("(ab|cd)+\\.(txt)"); // 可匹配ab.txt,cd.txt,abcde.txt等
  	cout<<boolalpha<<std::regex_match(strSrc, matchRes1, rePattern12)<<endl; // true
  	cout<<matchRes1[0]<<":"<<matchRes1.str(1)<<":"<<matchRes1.str(2)<<endl;  // abcdab.txt:ab:txt
  
  	// {n}表示匹配前面的字符或子表达式n次
  	strSrc = "abab.txt";
  	std::regex rePattern13("(ab){2}\\.(txt)"); // 只匹配abab.txt等
  	cout<<boolalpha<<std::regex_match(strSrc, matchRes1, rePattern13)<<endl; // true
  	cout<<matchRes1[0]<<":"<<matchRes1.str(1)<<":"<<matchRes1.str(2)<<endl;  // abab.txt:ab:txt
  
  	// [abc]表示匹配字符集中的字符
  	strSrc = "abcd.txt";
  	std::regex rePattern14("[abc]\\.(txt)"); // 只匹配a.txt,b.txt,c.txt
  	cout<<boolalpha<<std::regex_match(strSrc, matchRes1, rePattern14)<<endl; // flase
  
  	// [abc]+表示匹配1个或多个字符集中的字符
  	std::regex rePattern15("[abcde]+\\.(txt)"); // 只要.txt之前的字符属于字符集均匹配
  	cout<<boolalpha<<std::regex_match(strSrc, matchRes1, rePattern15)<<endl; // true
  	cout<<matchRes1[0]<<":"<<matchRes1.str(1)<<endl;  // abcd.txt:txt
  	
  	// 组([^aBcD]+)匹配到a即停止.[^aBcD]表示匹配非集合中的字符,[^A-Z]表示匹配A-Z之外的字符
  	strSrc = "AbCdA23ab4.txt";
  	std::regex rePattern16("([^aBcD]+)[^A-Z]+\\.(txt)"); // 只要.txt之前的字符属于字符集均匹配
  	cout<<boolalpha<<std::regex_match(strSrc, matchRes1, rePattern16)<<endl; // true
  	cout<<matchRes1[0]<<":"<<matchRes1.str(1)<<endl;  // AbCdA23ab4.txt:AbCdA23
  	
  	// [a-zA-Z0-9]{1,20},字符集内的字符,最少1次,最多20次.用来控制数字或字母的个数
  	strSrc = "AbCdA23ab4t";
  	std::regex rePattern17("[a-zA-Z0-9]{1,20}");
  	cout<<boolalpha<<std::regex_match(strSrc, rePattern17)<<endl; // true
  	
  	// regex对象默认是大小写敏感的,regex::icase即指定大小写不敏感
  	cout <<boolalpha<<regex_match("aaaAAA", regex("a*", regex::icase)) << endl;	 // true
  	cout <<boolalpha<<regex_match("aaaAAA", regex("a*")) << endl;				 // false
  	
	return 0;
}

4.2. regex_search

#include "stdafx.h"
#include <regex> 	// Must be included
#include <string>
#include <iostream>
using namespace std;


int _tmain(int argc, _TCHAR* argv[])
{
	std::string strSrc = "Color is #ffAB11";

  	// regex_serch并不要求整个匹配,只要匹配现有的pattern即为成功
  	std::regex rePattern1("#([a-f]{2})");
  	std::cout<<std::boolalpha<<std::regex_search(strSrc, rePattern1)<<std::endl;
  
  	// smatch存放匹配的结果,索引0存放的是完整的结果,索引1存放的是第1个()表达式匹配的结果
  	std::smatch matchRes2;
  	std::regex rePattern2("#([a-f]{2})");
  	std::cout<<std::boolalpha<<std::regex_search(strSrc, matchRes2, rePattern2)<<std::endl;
  	cout<<matchRes2[0]<<":"<<matchRes2.str(1)<<endl;  // #ff:ff
  
  	// [a-f]{2}表示有2个a-f之间的字符,[A-F]+表示有1个或多个A-F之间的字符
  	std::smatch matchRes3;
  	std::regex rePattern3("#([a-f]{2})([A-F]+)");
  	std::cout<<std::boolalpha<<std::regex_search(strSrc, matchRes3, rePattern3)<<std::endl;
  	cout<<matchRes3.str(0)<<":"<<matchRes3.str(1)<<":"<<matchRes3.str(2)<<endl;  // #ffAB:ff:AB
  	cout<<matchRes3[0]<<":"<<matchRes3[1]<<":"<<matchRes3[2]<<endl;				 // #ffAB:ff:AB
  	// prefix表示搜索匹配字符串之前未匹配的字符,suffix表示搜索匹配字符串之后未匹配的字符
  	cout<<matchRes3.prefix()<<":"<<matchRes3.suffix()<<endl;				     // Color is:11
  
  	// [0-9]*表示可以匹配0个或多个0-9的数字字符
  	std::smatch matchRes4;
  	std::regex rePattern4("#([a-f]{2})([A-F]+)([0-9]*)");
  	std::cout<<std::boolalpha<<std::regex_search(strSrc, matchRes4, rePattern4)<<std::endl;
  	cout<<matchRes4[0]<<":"<<matchRes4[1]<<":"<<matchRes4[2]<<":"<<matchRes4[3]<<endl;	// #ffAB11:ff:AB:11
  	// prefix表示搜索匹配字符串之前未匹配的字符,suffix表示搜索匹配字符串之后未匹配的字符(此处为空,即没有剩余未匹配的字符)
  	cout<<matchRes4.prefix()<<":"<<matchRes4.suffix()<<endl;				 // Color is:
  
  	// ^要求必须是从最开始即满足匹配
  	std::regex rePattern5("^#([a-f]{2})([A-F]+)([0-9]*)");
  	std::smatch matchRes5;
  	std::cout<<std::boolalpha<<std::regex_search(strSrc, matchRes5, rePattern5)<<std::endl; // false
  
  	// ^.+能够保证从最开始即满足匹配,$要求必须匹配到最后
  	std::regex rePattern6("^.+#([a-f]{2})([A-F]+)([0-9]*)$");
  	std::smatch matchRes6;
  	std::cout<<std::boolalpha<<std::regex_search(strSrc, matchRes6, rePattern6)<<std::endl; // true
  	
	return 0;
}

4.3. regex_replace

regex_replace要做两件事,首先是搜索匹配,然后将匹配到的字符按规则进行替换。

ECMAScript 扩展格式规则替换的字符
“$&”与整个正则表达式相匹配的字符序列
“$$”直接替换$
“$`”(美元符号后跟反撇号)与正则表达式相匹配的子序列之前的字符序列
“$’”(美元符号后跟正撇号)与正则表达式相匹配的子序列之后的字符序列
“$n”在 n 位置与捕获组相匹配的字符序列,其中 n 是介于 0 到 9 之间的数字
“$nn”在 nn 位置与捕获组相匹配的字符序列,其中 nn 是介于 10 到 99 之间的数字
#include "stdafx.h"
#include <regex> 	// Must be included
#include <string>
#include <iostream>
using namespace std;


int _tmain(int argc, _TCHAR* argv[])
{
	std::string strSrc = "Color is #ffAB11";

  	// 首先匹配所有的小写字母,然后将匹配的字符替换成指定字符*
  	std::regex rePattern1("[a-z]");
  	std::string strReplace1 = "*";
  	std::cout<<std::tr1::regex_replace(strSrc, rePattern1, strReplace1)<<std::endl; // C**** ** #**AB11
  
  	// $&表示匹配的字符,[$&]即替换为[匹配的字符]
  	std::string strReplace2 = "[$&]";
  	std::cout<<std::tr1::regex_replace(strSrc, rePattern1, strReplace2)<<std::endl; // C[o][l][o][r] [i][s] #[f][f]AB11
  
  	// $1 $2 $3$4表示正则表达式中对应的4个捕获组(子表达式).匹配之后,按要求显示4个捕获组内容
  	strSrc = "{0xff-0x11-0xab-0x21}";
  	std::regex rePattern3("\\{([a-z0-9]+)-([a-z0-9]+)-([a-z0-9]+)-([a-z0-9]+)\\}");
  	std::string strReplace3 = "$1 $2 $3$4";
  	std::cout<<std::tr1::regex_replace(strSrc, rePattern3, strReplace3)<<std::endl; // 0xff 0x11 0xab0x21
  	std::string strReplace4 = "$1 $3$4"; // 按格式显示1,3,4捕获组
  	std::cout<<std::tr1::regex_replace(strSrc, rePattern3, strReplace4)<<std::endl; // 0xff 0xab0x21
  
  	std::regex rePattern5("([a-z0-9]+)-([a-z0-9]+)-([a-z0-9]+)-([a-z0-9]+)");
  	std::string strReplace5 = "$`"; // 将匹配的内容替换成prefix
  	std::string strReplace6 = "$'"; // 将匹配的内容替换成suffix
  	std::cout<<std::tr1::regex_replace(strSrc, rePattern5, strReplace5)<<std::endl; // {{}
  	std::cout<<std::tr1::regex_replace(strSrc, rePattern5, strReplace6)<<std::endl; // {}}
  	
  	// 修改默认的flag参数为format_no_copy,即不拷贝prefix和suffix
  	std::cout<<std::tr1::regex_replace(strSrc, rePattern5, strReplace5, std::regex_constants::format_no_copy)<<std::endl; // {
  	std::cout<<std::tr1::regex_replace(strSrc, rePattern5, strReplace6, std::regex_constants::format_no_copy)<<std::endl; // }

	return 0;
}

4.4 sregex_iterator

sregex_iterator按正则表达式循环匹配整个字符串,获取并存放所有匹配结果。

#include "stdafx.h"
#include <regex> 	// Must be included
#include <string>
#include <iostream>
using namespace std;


int _tmain(int argc, _TCHAR* argv[])
{
	std::string strDst = "Quick brown fox.";

  	// 遍历整个字符串,匹配到空白时进入下一次匹配.匹配的结果存放在迭代器中
  	std::regex rePattern("[^\\s]+");
  	auto wordIt = std::sregex_iterator(strDst.begin(), strDst.end(), rePattern);
  
  	// 计算迭代器中存放的正确匹配的数量
  	std::cout<<"Found " << std::distance(wordIt, std::sregex_iterator())<<" words:\n";
  
  	// 遍历迭代器显示结果
  	for (; wordIt != std::sregex_iterator(); ++wordIt) 
  	{
  		std::cout<<wordIt->str()<<std::endl;
  	} 
  
  	strDst = "AB-12-AB AB-23-AC";
  	// (\\D{2})表示2个非数字,(\\d{2})表示2个数字,(\\1)表示第1个捕获组即(\\D{2})的内容
  	std::regex rePattern2("(\\D{2})-(\\d{2})-(\\1)");
  	std::regex_iterator<std::string::const_iterator> begin(strDst.begin(), strDst.end(), rePattern2);
  	for (auto iter = begin; iter != std::sregex_iterator(); iter++)
  	{
  		std::cout<<iter->str()<<std::endl;
  	}
  	// out: AB-12-AB

	return 0;
}

4.5 sregex_token_iterator

sregex_token_iterator按正则表达式循环匹配整个字符串,获取满足token要求的匹配结果并存放,相较于regex_iterator提高了效率。

#include "stdafx.h"
#include <regex> 	// Must be included
#include <string>
#include <iostream>
using namespace std;


int _tmain(int argc, _TCHAR* argv[])
{
	std::string strDst = "Quick brown fox.";

  	string data = "<person>\n"
  	" <first>Nico</first>\n"
  	" <last>Josuttis</last>\n"
  	"</person>\n";

  	// \\1指定第1个捕获组,即第1个子表达式
  	regex reg("<(.*)>(.*)</(\\1)>");
  
  	// 0是默认值,即完整的匹配结果.1是匹配结果的第1个捕获组,2是匹配结果的第2个捕获组(子表达式)
  	// iterate over all matches (using a regex_token_iterator):
  	sregex_token_iterator pos(data.cbegin(),data.cend(), // sequence
  							reg,                       // token separator
  							0);      // 0:full match, 1:First substring, 2:second substring
  	sregex_token_iterator end;
  	for ( ; pos!=end ; ++pos ) 
  	{
  		cout<<pos->str()<<endl;
  	}
  	// <first>Nico</first>
  	// <last>Josuttis</last>
  
  	// 第4个参数支持数组
  	int arrSubMatch[] = {1, 2};
  	sregex_token_iterator pos2(data.cbegin(),data.cend(), // sequence
  								reg,                       // token separator
  								arrSubMatch);      // 0:full match, 1:First substring, 2:second substring
  	sregex_token_iterator end;
  	for ( ; pos2!=end ; ++pos2 ) 
  	{
  		cout<<pos2->str()<<endl;
  	}
  	// first
  	// Nico
  	// last
  	// Josuttis
  
  	// -1指定显示匹配的prefix,即前面不匹配的
  	string names = "nico, jim, helmut";
  	regex sep("[ \t\n]*[,;.][ \t\n]*");  // separated by , ; or . and spaces
  	sregex_token_iterator iter(names.cbegin(),names.cend(),  // sequence
  							sep,                          // separator
  							-1);        // -1: values between separators
  	for ( ; iter != end; ++iter ) 
  	{
  		cout <<*iter<<endl;
  	}
  	// nico
  	// jim
  	// helmut

	return 0;
}

4.6 regex_error

正则表达式一写错,就容易导致崩溃,所以针对一些由用户编写正则表达式的情况,需要添加异常处理,防止崩溃。

#include "stdafx.h"
#include <regex> 	// Must be included
#include <string>
#include <iostream>
using namespace std;


int _tmain(int argc, _TCHAR* argv[])
{
	try 
  	{
  		// 正则表达式错误导致异常,需要捕获,否则会程序会崩溃
  		std::regex re("[a-b][a");
  	} 
  	catch (const std::regex_error& e) 
  	{
  		std::cout << "regex error caught:"<< e.what() <<std::endl;
  		if (e.code() == std::regex_constants::error_brack)
  		{
  			std::cout << "The code was error!\n";
  		}
  	}

	return 0;
}

5. 其他

5.1. 分组

小括号来指定子表达式(也叫做分组),然后就可以指定这个子表达式的重复次数了。(\d{1,3}.){3}\d{1,3}是一个简单的IP地址匹配表达式。

5.2. 后向引用

使用小括号指定一个子表达式后,匹配这个子表达式的文本可以在表达式或其它程序中作进一步的处理。默认情况下,每个分组会自动拥有一个组号,规则是:以分组的左括号为标志,从左向右,第一个分组的组号为1,第二个为2,以此类推。
后向引用用于重复搜索前面某个分组匹配的文本。例如,\1代表分组1匹配的文本。

5.3. 分组及捕获

表达式解释
(exp)匹配exp,并捕获文本到自动命名的组里
(?exp)匹配exp,并捕获文本到名称为name的组里
(?:exp)匹配exp,不捕获匹配的文本
(?=exp)匹配exp前面的位置
(?<=exp)匹配exp后面的位置
(?!exp)匹配后面跟的不是exp的位置
(?<!exp)匹配前面不是exp的位置
(?#comment)这种类型的组不对正则表达式的处理产生任何影响,只是为了提供让人阅读注释

5.4. 贪婪与懒惰

当正则表达式中包含能接受重复的量词(指定数量的代码,例如*,{5,12}等)时,通常的行为是匹配尽可能多的字符。考虑这个表达式:a.*b,它将会匹配最长的以a开始,以b结束的字符串。如果用它来搜索aabab的话,它会匹配整个字符串aabab。这被称为贪婪匹配。
有时需要懒惰匹配,也就是匹配尽可能少的字符。前面给出的量词都可以被转化为懒惰匹配模式,只要在它后面加上一个问号?。这样.?就意味着匹配任意数量的重复,但是在能使整个匹配成功的前提下使用最少的重复。如:a.?b匹配最短的,以a开始,以b结束的字符串。如果把它应用于aabab的话,它会匹配aab和ab。

表达式解释
*?重复任意次,但尽可能少重复
+?重复1次或更多次,但尽可能少重复
??重复0次或1次,但尽可能少重复
{n,m}?重复n到m次,但尽可能少重复
{n,}?重复n次以上,但尽可能少重复
  • 20
    点赞
  • 57
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: 在 C 语言中,运行正则表达式可能会卡住的原因可能有很多。其中一个原因可能是使用的正则表达式引擎在处理复杂的正则表达式时可能会出现“超时”的情况。 正则表达式是一种用来匹配文本模式的工具,它通常被用来在大型文本中搜索或替换特定的字符串。但是,如果正则表达式非常复杂或者匹配的文本非常大,那么使用正则表达式引擎处理这些表达式可能会占用大量的 CPU 时间,导致程序“卡住”。 此外,如果正则表达式中包含了一些“不优雅”的模式(例如,匹配的字符串中包含大量的重复字符),也可能会导致程序卡住。在这种情况下,可以尝试使用更有效的正则表达式来解决问题。 总的来说,如果你的程序在运行正则表达式时“卡住”,那么最好的解决办法就是尝试优化你的正则表达式,或者使用更快的正则表达式引擎来处理这些表达式。 ### 回答2: 运行正则表达式会卡住可能有以下几个原因: 1. 正则表达式的匹配模式过于复杂。某些正则表达式可能包含大量的逻辑判断、重复项匹配或回溯等操作,这会导致匹配过程非常耗时,进而导致卡顿。 2. 被匹配的文本过长或包含大量的匹配项。如果待匹配的文本非常庞大或者包含大量的匹配项,正则引擎需要不断地尝试不同的匹配组合,消耗大量的计算资源和时间,从而导致卡顿问题的产生。 3. 缺乏优化。有些正则表达式可能并没有经过充分的优化,导致在执行时效率低下。可以通过使用更精确的匹配规则、避免使用贪婪匹配、减少回溯等方法来优化正则表达式的性能。 4. 正则表达式引擎的实现问题。某些情况下,正则表达式引擎的实现可能存在漏洞或性能问题,导致在匹配过程中出现卡顿现象。 为避免正则表达式卡住的问题,可以尝试以下解决方案: 1. 对复杂的正则表达式进行简化或优化,避免不必要的重复匹配或回溯操作。 2. 尽量减少待匹配的文本长度,或者通过分块的方式进行匹配,减少单个匹配操作的复杂度。 3. 使用更高效的正则表达式引擎或库,可以选择一些经过性能优化的库来提升匹配速度。 4. 针对具体情况,可以考虑采用其他方法,如字符串操作或自定义解析器等,来代替正则表达式进行文本匹配。 综上所述,正则表达式可能会卡住的原因有多种,解决方法也因情况而异。需要根据具体情况分析和优化,以提高正则表达式的运行效率。 ### 回答3: C运行正则表达式卡住的原因可能有以下几点: 1. 正则表达式本身复杂度高:某些复杂的正则表达式可能会导致运行时间较长。正则表达式引擎需要逐个字符地匹配输入文本,如果正则表达式中包含多个嵌套的量词、回溯、或者复杂的匹配规则,会增加匹配的时间复杂度。 2. 输入文本过长:如果输入的文本非常大,比如几百兆或几个G的文本,正则表达式引擎需要逐个字符地遍历每个字符进行匹配,这样耗时较长,可能导致卡顿。 3. 正则表达式与输入文本不匹配:有时,正则表达式与输入文本不匹配会导致卡住的现象。正则表达式引擎会尝试所有可能的匹配,当正则表达式无法匹配到整个输入文本时,可能会进入无限循环或者时间复杂度较高的模式,造成运行卡住。 解决这个问题的方法可以有以下几种: 1. 优化正则表达式:简化或重写复杂的正则表达式,减少回溯和匹配次数,使用更高效的匹配语法。 2. 分割输入文本:如果输入文本过长,可以考虑将文本分割成小块逐个进行正则表达式匹配,降低时间复杂度。 3. 使用其他方法替代正则表达式:对于某些场景,可以考虑使用字符串处理函数或其他更高效的方式替代正则表达式匹配。 总之,正则表达式运行卡住可能是由于正则表达式本身复杂度高、输入文本过长或正则表达式与文本不匹配等原因造成的。要解决这个问题,可以优化正则表达式、分割文本或者使用其他方式替代正则表达式匹配。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值