正则表达式:回顾,以避免奇数个连续反斜杠

12

我有用户输入,在方括号内允许一些标签。我已经编写了正则表达式模式以查找和验证方括号内的内容。

在用户输入字段中,开放的括号可以用反斜杠进行转义,反斜杠也可以被另一个反斜杠进行转义 (\)。我需要使用回顾后发子模式来避免在开放的括号之前出现奇数个连续的反斜杠。

目前我必须处理类似这样的问题:

(?<!\\)(?:\\\\)*\[(?<inside brackets>.*?)]

它可以正常工作,但问题是这个代码仍然匹配在方括号前可能出现的连续反斜杠对(即使它们被隐藏了),并且向后查找只检查是否有另一个单独的反斜杠附加到对中(或直接附加到开放括号)。如果可能的话,我需要避免在向后查找组内全部匹配。

示例:

my [test] string is ok
my \[test] string is wrong
my \\[test] string is ok
my \\\[test] string is wrong
my \\\\[test] string is ok
my \\\\\[test] string is wrong
...
etc

我使用 PHP PCRE。


1
有奇数的数量是否有限制?1、3、5和7足以避免吗?我猜你会让2、4、6、8通过,对吗? - tchrist
1
@tchrist很不幸,它几乎是无限的。我在我的数据库中找到了一些连续40个斜杠的例子。有些人使用它们来制作ASCII“图画”,然后使用标签来着色某些元素或创建超链接。 - Wh1T3h4Ck5
2个回答

12

据我所知,PHP目前不支持变长的正向回顾后发断言,因此您不能使用简单的解决方案(?<![^\\](?:\\\\)*\\)

最简单的解决方法是将整个字符串匹配,而不仅仅是括号部分:

(?<!\\)((?:\\\\)*)\[(?<inside_brackets>.*?)]

现在的区别是,如果你在preg_replace中使用该正则表达式,那么你需要记得在替换字符串前加上$1前缀,以恢复反斜杠的存在。


我在手册中发现,回顾子模式内部存在一些限制,所以我猜你关于变长的想法是正确的。匹配整个字符串并仅提取括号内的内容不是问题。我目前正在这样做。某些REGEX风格允许在回溯中使用完整模式,例如.NET,但我想知道在PCRE中是否可能。顺便说一下,我正在preg_match_all()中使用该模式。无论如何,感谢您的答复。 - Wh1T3h4Ck5
不,PCRE 中不可能实现这一点;整个字符串匹配仅仅是解决此问题的一种变通方法。它提供了相同的功能,但代价是必须自己重新添加这些字符,并将额外匹配到的区域从可能的匹配中排除。由于所涉及的字符串部分只能包含反斜杠,因此在此处不存在括号匹配的问题。 - Etienne Perot
@Wh1T3h4Ck5:你接受的正则表达式(?<![^\\])<etc...>是不正确的。它在进行否定字符类(包含反斜杠)的负向回顾时,从而使其成为反斜杠的正向回顾。你需要使用(?<!\\)!我有权编辑这个答案。 - Tim Pietzcker
@TimPietzcker - 是的,我之前看到了那个,但是我接受了这个答案,因为在 PCRE 中没有解决我的问题,而这个答案的开头解释了原因。 - Wh1T3h4Ck5
@Tim,“(?<![^\])”与“(?<=\)”并不等价。前者如果字符串开头有匹配项,则会匹配,而后者需要至少一个中间字符(即反斜杠)的存在。是的,我知道你实际上使用的是“(?<!\)”,而不是“(?<=\)”(在我看来是正确的),但我不能让那个评论没有挑战性地过去。;) - Alan Moore

0

你可以完全不使用任何向后查找((\\\\|[^\\]) 选择项匹配除单个反斜杠以外的任何字符):

^(\\\\|[^\\])*\[(?<brackets>.*?)\] 

我需要反斜杠作为后顾组的一部分。我已经有很多不使用后顾的解决方案,其中一个在上面的问题中完美地工作。我不需要其他方法来完成同样的工作。Etienne Perot在他的答案中说,我所寻找的在PCRE中是不可能的,所以我有解决方案可以相信他是错误的(我非常怀疑),或者重新编写整个项目使用.NET,因为到目前为止,.NET只使用支持后顾全模式的REGEX flavor。 - Wh1T3h4Ck5
顺便说一下,你的示例有两个非常大的错误...1. 锚点 ^ 仅在字符串开头搜索,2. 组 (\\|[^\]) 要求在打开括号之前至少有一个字符,并且如果文档以标签开头,则不起作用。 - Wh1T3h4Ck5
@Wh1T3h4Ck5:上面发布的答案确实有后顾之忧,你认为这是什么:(?<!\\) ? - Scott Weaver
是的,伙计,这就是我接受那个答案的原因之一。顺便说一下,那个答案中的模式是我最初在问题中发布的一个精确副本。看看这个例子 "This [is] my [test][string]",告诉我你的模式是否匹配所有标签 - isthisstring?此外,我的问题说:“如果可能的话,我需要避免在后向组内使用所有反斜杠”,而你的答案并没有做到这一点。根据原始问题,我期望得到像“是的,可以跟随后向模式”或“不,不可能”的答案。就这么简单。 - Wh1T3h4Ck5
如果在那个测试字符串上运行,我的模式将匹配单词“is”,这正是它试图做的 - 我只能读到你实际写下的内容,而不是你脑海中的内容。还有一件事:我的答案并不是对Etienne答案的任何批评,也不是建议您不要使用后顾之约,而仅仅是提供一个不同的视角。 - Scott Weaver
显示剩余2条评论

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