C++与正则表达式入门 (7)

例如,特殊字符-只有在字符组[...]内部才是元字符,否则它只能匹配普通的连字符符号。并且,即便在字符组内部,如果连字符是在开头,它依然是一个普通字符而不是表示一个范围。

相反的,问号?和点号.不在字符组内部的时候才是特殊字符。因此[?.]中的这两个符号仅仅代表这两个字符自身。

还有,字符^出现在字符组中的时候表示的是否定,例如:[a-z]和[^a-z]表示的是正好相反的字符集。但是当字符^不是用在字符组中的时候,它是一个,具体内容下文会说到。

量词的占有欲

还是以content.txt的内容为基础,现在假设我们的目标是:找出所有双引号中的内容。

根据之前的知识,你可能很轻松就写出了下面这个正则表达式:

regex content_regex("\"(.+)\"");

两边的双引号通过反斜杠转义

待捕获的内容通过圆括号形成分组

双引号中可以是任意内容,因此使用.+

但是当你运行程序的时候却发现它可能有点问题。它捕获的结果是:

"find" or "find and replace"

为什么?其实很简单,因为双引号本身也可以与.匹配。上面这个正则表达式的含义是:匹配一个两端是双引号,中间是任意文字的内容。

当然,你马上想到一个改进方法那就是:将正则表达式圆括号中的.+改为[^"]+,它的含义是:一个或多个非双引号字符。这么做是可以的。但其实我们还有更好的做法。

我们再回头看一下原先的正则表达式,不考虑分组和转义,它可以写成:".+"。其实我们知道下面这三个字符串都是与其匹配的:

"find"

"find and replace"

"find" or "find and replace"

而将整个文本交给正则表达式的时候,它找出了最长的那个串。可见,原先的正则表达式太过“贪婪”(greedy)。是的,量词在默认情况都是贪婪的。即:它们会尽可能多的占有内容。

那我们能不能控制量词让其尽可能少的占有内容,只要满足匹配要求就可以呢?

答案是肯定的,而且做法很简单:在量词的后面加上一个?。即,将圆括号中.+修改为.+?即可。量词的默认形式称之为“匹配优先量词”,现在这种写法称之为“忽略优先量词”。

现在它找到的是下面两个匹配:

"find" "find and replace"

小结一下:

匹配优先量词:*,+,?,{num, num}

忽略优先量词: *?,+?,??,{num, num}?

锚点

锚点是一类特殊的标记,它们不会匹配任何文本内容,而是寻找特定的标记。你可以简单理解为它是原先表达式的基础上增加了新的匹配条件。如果条件不满足,则无法完成匹配。

锚点主要分为三种:

行/字符串的起始位置:^,行/字符串的结束位置:$

单词边界:\b

环视 ,见下文

例如:

正则表达式^\d+在字符串"123abc"中能找到匹配,在字符串"abc123"却找不到。

正则表达式some\b在字符串"some birds"中能找到匹配,在字符串"sometimes wonderful"中却找不到。

下面是代码示例:

#include <iostream> #include <regex> using namespace std; void findIn(const char* content, const char* reg_ex) { cout << "Search '" << reg_ex << "' in '" << content << "': "; smatch match; string s(content); regex reg(reg_ex); if(regex_search(s, match, reg)) { cout << match[0] << endl; } else { cout << "NOTHING" << endl; } } int main() { findIn("123abc", "^\\d+"); findIn("abc123", "^\\d+"); cout << endl; findIn("some birds", "some\\b"); findIn("sometimes wonderful", "some\\b"); return 0; }

它的输出如下:

Search '^\d+' in '123abc': 123 Search '^\d+' in 'abc123': NOTHING Search 'some\b' in 'some birds': some Search 'some\b' in 'sometimes wonderful': NOTHING 环视

现在假设我们有下面两个需求:

匹配出所有sometimes中的前四个字符“some”

匹配出所有的单词some,但是要排除掉“some birds”中的“some”

对于第一个问题,我们可以分两步:先找出所有的单词sometimes,然后取前四个字符。对于第二个问题,我们可以先找出所有的单词“some”,然后把后面是“birds”的丢掉。

以上的解法都是分两步完成。但实际上,借助环视(lookaround)我们可以一步就完成任务。

环视是对匹配位置的附加条件,只有条件满足时才能完成匹配。环视有:顺序(向右),逆序(向左),肯定和否定一共四种:

类型 正则表达式 匹配条件
肯定顺序环视   (?=...)   子表达式能够匹配右侧文本  
否定顺序环视   (?!...)   子表达式不能匹配右侧文本  
肯定逆序环视   (?<=...)   子表达式能够匹配左侧文本  
否定逆序环视   (?<!...)   子表达式不能匹配左侧文本  

C++中的环视只支持顺序环视,不支持逆序环视。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wssgxf.html