青藤实验室|文

概述

正则表达式,又称规则表达式。(英语:Regular Expression,在代码中常简写为regex、regexp或RE),计算机科学的一个概念。正则表达式通常被用来检索、替换那些符合某个模式(规则)的文本。

标准匹配量词(?、*、+、以及{min,max})都是“匹配优先(greedy)”的。如果用这些量词来约束某个表达式,例如‘a?’中的‘a’,‘[0-9]+’中的‘[0-9]’,在匹配成功之前,进行尝试的次数是存在上限和下限的。标准匹配量词的结果“可能”并非所有可能中最长的,但它们总是尝试匹配尽可能多的字符,直到匹配上限为止。

而忽略优先,则是在原本的匹配优先量词后加一个问号即可,即在*、+、?、{min,max}后加入?即可。

匹配优先:尽可能多的匹配

忽略优先:尽可能少的匹配

要注意的是,DFA不支持忽略优先。

0x01 匹配优先与忽略优先

匹配优先量词:*、+、?、{min,max}

忽略优先量词:*?、+?、??、{min,max}?

0x02 例子

1.匹配优先:

我们来看‘^w+d’匹配‘1a2b2c2de’的过程。首先‘w+’匹配整个字符串之后,然后为了匹配‘d’要求‘w+’释放一个字符‘e’(最后的字符)。看是否能让‘d’匹配,如果不能匹配,‘w+’必须继续“交还”字符,直到能匹配‘d’为止,如果所有的字符都交还完后还是不能匹配,则匹配失败。




java 正则单引号双引号转移 正则表达式双引号_正则不能全为某个值


2.忽略优先:

而对于忽略优先,总是匹配尽可能少的字符,先用‘w’来匹配‘1a2b2c2de’中的‘1’,此时,‘+?’又必须选择,是继续进行匹配,还是忽略?因为它是忽略优先的,会首先选择忽略。接下来的‘d’不能匹配后面的‘a’,所以‘w+?’会继续尝试未匹配的‘a’,然后看后面的字符能否匹配‘d’,直到能匹配‘d’或尝试所有可能后报告匹配失败。


java 正则单引号双引号转移 正则表达式双引号_正则不能全为某个值_02


3.如果用‘^w+(d+)’和^w+?(d+)来匹配‘copyright2019’,括号会捕获到什么?

‘^w+(d+)’匹配时为了满足‘d+’的匹配,必须交还一些字符。释放的字符是最后的‘9’,之后‘9’能够由‘d’匹配。‘d’由‘+’量词进行修饰,所以现在还做到了最小匹配的可能,此时没有“必须”匹配的元素,所以‘w’不会被迫交出‘1’。匹配优先的结构只会在被迫的情况下交还字符。所以,最终$1的值是‘9’:


java 正则单引号双引号转移 正则表达式双引号_正则不能全为某个值_03


‘^w+?(d+)’匹配时会先匹配‘copyright2019’中的‘c’,因为它是忽略优先的,会首先选择忽略,接下来‘d’不能匹配后一个字符‘o’,所以‘w+’会继续匹配未匹配的‘o’,在这个过程重复9次之后,‘w+’最终匹配了‘copyright’,此时,接下来的‘2019’都能匹配,所以最终$1的值是‘2019’:


java 正则单引号双引号转移 正则表达式双引号_正则不能全为某个值_04


4.对于匹配双引号文本:

首先想到的可能就是‘ ".*" ’,在最开始的双引号匹配之后,‘.*’能够匹配任何字符,所以它会一直匹配到字符串的末尾。为了让最后的双引号能够匹配,‘.*’不会交还字符(或者更确切的说,是正则引擎强迫它回退),直到满足为止。最后,这个正则表达式的结果就是:


java 正则单引号双引号转移 正则表达式双引号_正则不能全为某个值_05


原本希望匹配到的是"McDonald's"和"makudonarudo",然而这显然不是我们所期望的结果,我们希望匹配到的不是双引号之间的“任何文本”,而是“除双引号以外的任何文本”。如果用‘[^"]*’替代‘.*’,结果是:


java 正则单引号双引号转移 正则表达式双引号_正则不能全为某个值_06


用排除法时注意,因为在大多数的流派中,‘[^"]*’能够匹配换行符,而点号则不能:


java 正则单引号双引号转移 正则表达式双引号_正则表达式匹配引号_07


如果不想让表达式匹配换行符,可以用‘[^"]’:


java 正则单引号双引号转移 正则表达式双引号_正则不能全为某个值_08


也可以使用忽略优先量词:


java 正则单引号双引号转移 正则表达式双引号_正则表达式匹配引号_09


5.对于多字符“引文”

与双引号字符的例子一样,使用“.*”匹配多字符引文也会出错,‘.*’中匹配优先‘.*’会一直匹配到该行的结尾字符,回溯只会进行到最后一个‘’,而不是与匹配开头的‘’相对应的‘’:


java 正则单引号双引号转移 正则表达式双引号_正则不能全为某个值_10


我们可以使用忽略优先量词‘.*?’来进行匹配:


java 正则单引号双引号转移 正则表达式双引号_正则表达式匹配引号_11


当然,错误的使用忽略优先量词有时候也会出现问题,如果用‘.*?’来匹配‘...Billionsand Zillions


java 正则单引号双引号转移 正则表达式双引号_正则不能全为某个值_12


显然这不是用户所期望的结果,这时候如果支持否定环视,我们就能得到与排除型字符组相当的结果,只有当‘’不在字符串中的当前位置才能匹配成功。


java 正则单引号双引号转移 正则表达式双引号_正则表达式匹配引号_13


这时候不管是用匹配优先量词还是忽略优先量词都可以。

0x03 正则表达式系列

正则表达式之环视功能

0x04 参考

《精通正则表达式》(第三版)

https://regexr.com/


java 正则单引号双引号转移 正则表达式双引号_正则不能全为某个值_14