以下正则表达式是如何工作的?

4

假设我有一个字符串,我想从开头的双引号解析到结尾的双引号:

asdf"pass\"word"asdf

我很幸运地发现以下PCRE可以匹配从开头的双引号到结束的双引号,同时忽略中间的转义双引号(以正确解析逻辑单元):

".*?(?:(?!\\").)"

匹配:

"pass\"word"

然而,我不知道为什么这个PCRE可以正确匹配开头和结尾的双引号。
我了解以下内容:
" = 双引号字面值
.*? = 懒惰匹配零个或多个任意字符
(?: = 开始非捕获组
(?! ") = 断言无法匹配字面上的\"
. = 单个字符
) = 结束非捕获组
" = 双引号字面值
看起来一个单独的字符和负向先行断言是同一个逻辑组的一部分。对我来说,这意味着PCRE的意思是“从双引号开始匹配零个或多个任意字符,只要在字符后面没有 \",然后匹配一个以上的字符和一个单引号。”
但是,按照这种逻辑,PCRE根本不会匹配该字符串。
有人能帮我理解吗?
2个回答

2
“如果你将非捕获组改为捕获组,就更容易理解了。”
“惰性匹配通常是一次向前移动一个字符(与贪婪匹配尽可能多的内容然后放弃必要的内容不同)。但是它会“向前移动”,直到满足其后面所需的模式部分,这是通过让.*?匹配到r,然后让否定前瞻+.匹配到d来实现的。”
“更新:你在评论中问道:”
“为什么它能够匹配到r?难道否定前瞻不应该防止它在字符串中超过\"吗?顺便感谢你的帮助,让我理解了。”
“不是的,因为它并不是被否定前瞻匹配的。这就是为什么我建议你将非捕获组改为捕获组,以便你可以看到是.*?匹配到了\",而不是(?:(?!\\").)。”
".*?" 有潜力匹配整个字符串,正则表达式引擎利用这一点满足匹配其余模式的要求。
更新2:
实际上,这与执行以下操作相同:".*?[^\\]",这可能更容易理解。
稍微好一点的模式是使用否定回顾,如下所示:".*?(?

为什么它会跟 r 匹配?负向先行断言不应该阻止它通过字符串中的 \" 吗?顺便感谢帮我理解。 - Nolan
bash不使用Perl风格的正则表达式,只使用POSIX定义的扩展正则表达式 - chepner
@chepner,我的例子展示了使用-P的方法。 - CrayonViolent

0

在Crayon Violent的解释下没有什么要补充的,只是需要一些澄清和匹配双引号之间子字符串的方法(有时需要转义双引号)。

首先,在您的问题中使用缩写词“PCRE”(Perl Compatible Regular Expression),这是一个特定的正则表达式引擎的名称(及其语法),但通常指代描述一组其他字符串的正则表达式模式(无论使用哪个正则表达式引擎)。

使用Bash:

A='asdf"pass\"word"asdf'
pattern='"(([^"\\]|\\.)*)"'

[[ $A =~ $pattern ]]
echo ${BASH_REMATCH[1]}

你也可以使用这个模式:pattern='"(([^"\\]+|\\.)*)"' 使用 PCRE 正则表达式引擎,你可以使用第一个模式,但最好以更有效的方式重写它:
"([^"\\]*+(?:\\.[^"\\])*+)"

请注意,对于这三种模式都不需要任何环视。它们能够处理任意数量的连续反斜杠:"abc\\\"def" (一个字面上的反斜杠和一个转义引号)"abcdef\\\\" (两个字面上的反斜杠,引号未转义)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接