具有奇怪行为的正则表达式:将字符串与反向引用匹配以允许转义以及单引号和双引号

Posted

技术标签:

【中文标题】具有奇怪行为的正则表达式:将字符串与反向引用匹配以允许转义以及单引号和双引号【英文标题】:RegEx with strange behaviour: matching String with back reference to allow escaping and single and double quotes 【发布时间】:2011-04-30 02:39:27 【问题描述】:

匹配一个允许转义的字符串并不难。 看这里:http://ad.hominem.org/log/2005/05/quoted_strings.php。 为简单起见,我选择了将字符串分成两个“原子”的方法:一个字符“不是引号或反斜杠”,或者一个反斜杠后跟任何字符。

"(([^"\\]|\\.)*)"

现在明显的改进是,允许不同的引号和使用反向引用。

(["'])((\\.|[^\1\\])*?)\1

多个反斜杠也被正确解释。

现在到了奇怪的部分:我必须解析一些像这样的变量(注意第一个变量值中缺少的反斜杠):

test = 'foo'bar'
var = 'lol'
int = 7

所以我写了一个相当的表达式。我发现它的以下部分没有按预期工作(与上述表达式的唯一区别是附加的“([\r\n]+)”):

(["'])((\\.|[^\1\\])*?)\1([\r\n]+)

尽管缺少反斜杠,'foo'bar' 是匹配的。我为此使用了 gskinner 的 RegExr(在线工具),但 PHP (PCRE) 具有相同的行为。

要解决此问题,您可以通过将反向引用替换为 '.然后它按预期工作。 这是否意味着在这种情况下反向引用实际上不起作用?这与换行符有什么关系,没有它也能工作?

【问题讨论】:

字符类中不存在反向引用。 [^\1\\\] 匹配“不是反斜杠或 1 的每个字符”或“不是反斜杠或 \001 的每个字符”,具体取决于您选择的语言如何解释 \1 【参考方案1】:

你不能在字符类中使用反向引用;在这种情况下,\1 将被解释为八进制 1(至少在某些正则表达式引擎中,我不知道这是否普遍正确)。

因此请尝试以下方法:

(["'])(?:\\.|(?!\1).)*\1(?:[\r\n]+)

或者,作为一个冗长的正则表达式:

(["'])       # match a quote
(?:          # either match...
 \\.         # an escaped character
 |           # or
 (?!\1).     # any character except the previously matched quote
)*           # any number of times
\1           # then match the previously matched quote again
(?:[\r\n]+)  # plus one or more linebreak characters.

编辑:删除了一些不必要的括号并将一些更改为非捕获括号。

您的正则表达式坚持要在匹配的字符串之后找到至少一个回车 - 为什么?如果它是文件的最后一行怎么办?或者如果字符串后面有注释或空格?你可能应该完全放弃那部分。

还请注意,您不必使 * 变得懒惰以使其工作 - 正则表达式不能跨越未转义的引号字符 - 并且您不必在第二部分检查反斜杠由于所有反斜杠都已被替换(?:\\.|(?!\1).) 的第一部分铲除,所以替换。这就是为什么这部分必须放在第一位的原因。

【讨论】:

以上是关于具有奇怪行为的正则表达式:将字符串与反向引用匹配以允许转义以及单引号和双引号的主要内容,如果未能解决你的问题,请参考以下文章

正则表达式;反向引用字符集中不匹配的字符

正则表达式反向引用乘法[重复]

正则表达式 - 分组分组引用反向引用多选非捕获分组

JS正则表达式之--?:

引用类型--RegExp类型

正则表达式 -- 元字符(转)