首页 > 代码库 > 《精通正则表达式》笔记 --- 选择引号内的文字

《精通正则表达式》笔记 --- 选择引号内的文字

这个例子出自《精通正则表达式》,做一下笔记帮助理解和记忆。

第一版

最简单的case就是考虑包含一对引号,那么写出来的表达式应该是这样的:

".*"

但是这个未免太简单了吧,会有啥问题呢?假如输入的字符串长这样结果就会出问题拉。see...

Input String: "Hello" and "World" Regex: ".*" Match: "Hello" and "World"

为什么会全部匹配到呢?这是因为 * 是一个greedy(匹配优先)的量词,我觉得英文的意思更容易帮助我们理解。这意味着它会首先会‘贪婪‘得把所有的字符匹配完,匹配到最后一个字符发现没有字符可以匹配了,于是开始匹配下一个引号,它会首先回朔到最后一个引号前,然后开始匹配引号匹配引号,发现可以匹配,然后就完成了。这就是为啥整个字符串都被匹配了。

第二版

既然是因为greedy的量词导致的这个问题,那我们将其给成lazy(忽略优先)的量词 ----- *? 。当一个量词是忽略优先的话,那在匹配的时候,引擎会选择忽略这个忽略优先量词修饰的字符去匹配下一个字符,如果匹配则继续,如果不匹配则返回来匹配这个忽略优先量词修饰的字符。

看一下这个例子,将表达式稍作修改,匹配结果就变鸟。

Input String: "Hello" and "World" Regex: ".*?" Match-1: "Hello" Match-2: "World"

好了,似乎这个版本已经圆满完成我们的任务啦。

第三版

那我们将需求继续变化一下,在程序的世界里有一种东西叫做转移字符,比如有个字符串长这样子\"Hello, World!\"+\"。 直接上最终正则表达式。

Input String: \"Hello, World!\"+\" Regex: "(\\.|[^\\"])*" Match-1: No Match

正则表达式中关键的是这个(\\.|[^\\"]),这个括号在正则表达式里是起到一个多选结构的作用,表示匹配括号内任意一个子表达式即匹配。这个括号里面由两部分,第一部分是\\.,第二部分是[^\\"]

\\.

\\.能够匹配任何的转义字符,严格来说是匹配任意\以及其后面的字符,即使它们不是真正的转义字符。

[^\"]

这个输入字符串最后一个引号是转移的引号,所以这个正则表达式没有匹配到任何的结果。这个结果是对的,这个功劳就要归结到这个[^\\"],它的意思是匹配非\"的任意字符。当引擎匹配到字符串最后发现"还未匹配,于是进行回溯,回溯到?那个位置\"Hello, World!\"+ ? \"。引擎会尝试去匹配[^\\"],发现匹配失败,于是失败!

假如我们将[^\\"]换成[^"],那么在刚才那个位置的时候,[^"]就会匹配\字符,然后\之后的"会被外层的引号匹配,于是它的结果就是被匹配了。这显然是很奇怪的,当然如果你想要这么匹配也可以。

其他版本

还有一种方式是利用正则表达式里面的固化分组(Atomic grouping)或者占有优先量词(Possessive)。固化分组:"(?>(\\.|[^"])*),占有优先量词:"(\\.|[^"])*+"。在固化分组或者占有优先量词的匹配过程中,在固化分组内或者占有优先量词修饰的组内,如果一个字符已经被匹配那么之前的状态将会被舍弃,就防止它进行回溯。

在这个例子中,\"Hello, World!\"+\",当匹配到这个位置的时候(\"Hello, World!\"+\" ?)发现没有办法继续匹配下去了,引擎会选择直接匹配失败,而不是回溯到之前的备用状态进行新的匹配。

关键的概念

例子虽然简单,但是出现了正则表达式中很多关键的概念,预知详情,请阅读《精通正则表达式》

  • 匹配优先量词(Greedy quantifier)
  • 忽略优先量词(Lazy quantifier)
  • 占有优先量词(Possessive quantifier)
  • 固化分组(Atomic grouping)
  • 字符组(Character Classes)
  • 多选结构(Alternation)

本文纯属个人读书笔记,如有错误概不负责哟~~~