如何匹配带有可选引号的模式?

21

如何编写一个正则表达式来匹配一个可以包含引号的模式,但是如果有引号,则必须在开头和结尾处具有匹配的引号?

"?(pattern)"?

这样做是不起作用的,因为它会允许以引号开头但没有以引号结尾的模式。

"(pattern)"|(pattern)

这样可以运行,但是有些重复。是否有更好的方法来避免重复模式?


1
你在使用哪种正则表达式呢?.Net允许这种“括号匹配”,但其他类型的不支持。 - cristobalito
相关探讨:https://dev59.com/X07Sa4cB1Zd3GeqP69EB - polygenelubricants
5个回答

28

通过利用反向引用条件语句,可以获得不重复的解决方案:

/^(")?(pattern)(?(1)\1|)$/

匹配:

  • pattern
  • "pattern"

不匹配:

  • "pattern
  • pattern"

这个正则表达式有点复杂。它首先查找可选的引号,并将其放入第一个回溯引用中(如果找到)。然后查找您的模式。然后使用条件语法来表示“如果再次找到回溯引用1,则匹配它,否则不匹配”。整个模式都被锚定,这意味着它需要单独出现在一行上,以便不会捕获未匹配的引号(否则,pattern中的pattern"将匹配)。

请注意,对于条件语法的支持因引擎而异,更冗长但重复的表达式将得到更广泛的支持(并且可能更容易理解)。


更新:这个正则表达式的简化版本是/^(")?(pattern)\1$/,它不需要条件语法。当我最初测试时,使用的测试工具给了我一个错误的负面结果,导致我将其排除在外(糟糕!)。

我会保留使用条件语法的解决方案供后人和感兴趣的人参考,但这个更简单的版本在更多引擎中可能更容易使用(只使用了回溯引用这一功能,可能不受支持)。


1
@rubber 曾经,我知道如何进行递归正则表达式,但我想为了人类的利益而遗忘了。 - Daniel Vandersluis
@Daniel:如果你忘记条件语句,人类可能也不会介意。^("?)pattern\1$可以很好地工作。(@wuputah的删除答案没有起作用,因为它没有被锚定。而且@Tim,贪婪量词/原子组是不需要的。) - Alan Moore
@Alan 看起来我之前使用的正则表达式测试工具有一个漏洞,导致我对 ^("?)pattern\1$ 得到了错误的结果,这也是我一开始尝试条件语句解决方案的原因...糟糕。 - Daniel Vandersluis
我说忘记条件语句只是半开玩笑。我早就学过它们,但后来我主要使用Java——它从未支持条件语句——我从未想念它们。虽然听起来很不错,但几乎总有更好的方法。 - Alan Moore
Java不支持条件构造。我使用**(['"]??)(pattern)\1(?=[^'"])**。 - lunicon
显示剩余2条评论

2
这也很简单:(".+"|.+)。确保第一个匹配项带引号,第二个不带引号。

我很惊讶为什么没有人喜欢这个。它非常简单而优雅。 - humility

1

根据您使用的编程语言,您应该能够使用反向引用。例如:

(["'])(pattern)\1|^(pattern)$

这样,您要求要么没有引号,要么在两端使用相同的引号。


0

这应该可以使用递归正则表达式(需要更长时间才能正确)工作。与此同时:在Perl中,您可以构建一个自修改的正则表达式。我将其作为学术示例留下;-)

my @stuff = ( '"pattern"', 'pattern', 'pattern"', '"pattern'  );

foreach (@stuff) {
   print "$_ OK\n" if /^
                        (")?
                        \w+
                        (??{defined $1 ? '"' : ''})
                       $
                      /x
}

结果:

"pattern" OK
pattern OK

0

通常@Daniel Vandersluis的回答是可行的。然而,一些编译器如果可选组(“)为空,则无法识别它,因此它们无法检测到反向引用\1。

为了避免这个问题,更健壮的解决方案是:

/^("|)(pattern)\1$/

然后编译器将始终检测第一组。如果表达式中有一些前缀并且您想先捕获它,则可以修改此表达式:

/^(key)=("|)(value)\2$/

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