前言
正则表达式,又称规则表达式,是计算机科学的一个概念。正则表达式通常被用来检索、替换那些符合某个模式(规则)的文本。下文将对正则表达式基本语法进行介绍以及在C++11中如何使用正则表达式。
一.正则表达式语法
正则表达式由一系列组件构成,这些组件可以是单个的字符、字符集合、字符范围、字符间的选择或者所有这些组件的任意组合,描述在搜索文本时要匹配的一个或多个字符串。正则表达式作为一个模板,将某个字符模式与所搜索的字符串进行匹配。
01普通字符
普通字符包括没有显式指定为元字符的所有可打印和不可打印字符。这包括所有大写和小写字母、所有数字、所有标点符号和一些其他符号。
02
非打印字符
非打印字符也可以是正则表达式的组成部分。下表列出了表示非打印字符的转义序列:
03
特殊字符
特殊字符就是有特殊含义的字符,比如要f+eature中的+表示前面的字符必须至少出现一次,也就是说可以匹配feature,ffffeature等, 如果需要匹配特殊含义的字符,就需要使用转义,也就是在特殊字符前面加反斜杠。
下表列出了正则表达式中的特殊字符:
04
限定符
限定符用来指定正则表达式的一个给定组件必须要出现多少次才能满足匹配。有 * 或 + 或 ? 或 {n} 或 {n,} 或 {n,m} 共6种。
05
定位符
定位符用来描述字符串或单词的边界,^ 和 $ 分别指字符串的开始与结束,\b 描述单词的前或后边界,\B 表示非单词边界。
正则表达式的定位符有:
06
选择
当想要对多个字符进行获取的时候,如果要对多个字符进行重复怎么办呢?此时我们就要用到分组,用圆括号将所有选择项括起来,相邻的选择项之间用|分隔。例如在字符串"The rime of the Ancyent Mariner"中找出the出现过多少次(包括THE, The和the的形式),可通过正则表达式“(THE|The|the)”进行匹配。使用圆括号将正则表达式模式或部分模式括起来进行分组会把所有的匹配会都被保存起来,如果需要剔除某种特殊情况,可以通过?:来实现。这里的“?:”被称作为非捕获分组,非捕获分组和捕获分组唯一的区别在于,非捕获分组匹配的值不会保存起来。例如匹配indestry或者indestries,可以使用indestr(y|ies)或者indestr(?:y|ies)
07
反向引用
捕获组捕获到的内容,不仅可以在正则表达式外部通过程序进行引用,也可以在正则表达式内部进行引用,这种引用方式就是反向引用。
捕获组在匹配成功时,会将子表达式匹配到的内容,保存到内存中一个以数字编号的组里,可以简单的认为是对一个局部变量进行了赋值,这时就可以通过反向引用方式,引用这个局部变量的值。一个捕获组在匹配成功之前,它的内容可以是不确定的,一旦匹配成功,它的内容就确定了,反向引用的内容也就是确定的了。
反向引用必然要与捕获组一同使用的,如果没有捕获组,而使用了反向引用的语法,不同语言的处理方式不一致,有的语言会抛异常,有的语言会当作普通的转义处理。
例如有如下一个字符串。
Is is the cost of of gasoline going up up?
这个字符串中有多个重复的单词。需要逐个搜索以查找是否重复,但如果使用反向引用,就很容易进行查找。
/\b([a-z]+) \1\b/gi
[a-z]+ 指定的包括一个或多个字母。第二部分的\1是对以前捕获的子匹配项的引用,字边界元字符\b确保只检测整个单词。后面的全局标记g表示全局查找尽可能多的匹配。结尾处的i表示不区分大小写。
08
正则运算符优先级
正则表达式从左到右进行计算,并遵循优先级顺序,这与算术表达式非常类似。
相同优先级的从左到右进行运算,不同优先级的运算先高后低。下表从最高到最低说明了各种正则表达式运算符的优先级顺序:
二.C++11如何使用正则表达式
早期C++标准库中是没有提供正则表达式处理的功能,从C++11开始在标准库中提供了正则表达式处理类,但是需要注意的一点是GCC在4.8中说明提供了C++11的全不特性,但实际上针对于正则表达式的类只有相关的声明,如果使用正则会引发异常。GCC需采用4.9以上版本才支持全特性的正则表达式。
C++11提供了多个类用于正则表达式的处理。下面逐一介绍:
01std::basic_regex
类模板 basic_regex 提供了一套正则表达式的通用框架,用于封装正则表达式对象,标准库提供了常用的特化
正则表达式有种文法,对于相同的正则表达式,不同的处理文法得到的结果也不尽相同,C++提供了六种文法选项,ECMAScript, basic, extended, awk, grep, egrep 必须选取至多一个文法选项。若不选取文法选项,则设定为选取 ECMAScript,针对各种文法的异同可参考文法文档,不在此赘述。
如下例子可展示不同文法其匹配的结果也会有差异。
由于ECMAScript和POSIX处理规则的不同,所以处理结果也不同,输出如下:
02
std::sub_match
类模板 sub_match 用于保存匹配的字符序列。匹配是目标范围中匹配正则表达式的 [begin, end) 对,只有默认构造函数是可公开访问的。在正则处理时,把处理结果存放在sub_match 的std::match_results容器中。
03
std::match_results
类模板 std::match_results 用于保存正则表达式匹配结果的字符序列集。
这是特殊的具分配器容器。它只能默认创建、从std::regex_iterator获得,或通过 std::regex_search或 std::regex_match修改。std::match_results 保有 std::sub_match,它们每个都是一对指向匹配的原初字符序列中的迭代器,所以如果原字符序列被销毁或指向它的迭代器无效,则访问std::match_results会造成不可预期的行为。
match_result 中所含的首个 sub_match (下标 0 )始终表示 regex 所做的目标序列内的完整匹配,而后继的 sub_match 表示按顺序对应分隔正则表达式中子表达式的左括号的子表达式匹配。
04
std::regex_match
std::regex_match尝试匹配一个正则表达式到整个字符序列,regex_search尝试匹配一个正则表达式到字符序列的任何部分,regex_replace以格式化的替换文本来替换正则表达式匹配的出现位置。
输出
05
std::regex_search
std::regex_search用于尝试匹配一个正则表达式到字符序列的任何部分,下面是具体的使用方法。
输出
06
std::regex_replace
std::regex_replace用于以格式化的替换文本来替换正则表达式匹配的出现位置,下面是具体的使用方法。
输出