如何编写一个正则表达式来匹配可以包含引号的模式,但如果包含引号,则必须在开头和结尾具有匹配的引号?
"?(pattern)"?
不起作用,因为它将允许以引号开头但不以引号结尾的模式。
"(pattern)"|(pattern)
可以工作,但有重复性。有没有更好的方法来做到这一点而不重复模式?
您可以通过使用 backreferences 和 conditionals 获得无需重复的解决方案:
/^(")?(pattern)(?(1)\1|)$/
比赛:
不匹配:
然而,这种模式有些复杂。它首先查找可选引用,如果找到,则将其放入反向引用 1 中。然后它会搜索您的模式。然后它使用条件语法来表示“如果再次找到反向引用 1,则匹配它,否则不匹配”。整个模式是anchored(这意味着它需要单独出现在一行上),这样不匹配的引号就不会被捕获(否则
pattern
中的pattern"
会匹配)。
请注意,对条件的支持因引擎而异,更详细但重复的表达式将得到更广泛的支持(并且可能更容易理解)。
更新: 这个正则表达式的一个更简单的版本是
/^(")?(pattern)\1$/
,它不需要条件。当我最初测试这个时,我使用的测试仪给了我一个假阴性,这导致我对它打折扣(哎呀!)。
为了后代和兴趣,我将保留带有条件的解决方案,但这是一个更简单的版本,更有可能在更广泛的引擎中工作(反向引用是此处使用的唯一可能不受支持的功能)。
这也很简单:
(".+"|.+)
。确保第一个匹配项带引号,第二个匹配项不带引号。
根据您使用的语言,您应该能够使用反向引用。像这样,说:
(["'])(pattern)\1|^(pattern)$
这样,您就需要要么没有引号,要么两端使用相同的引号。
这应该与递归正则表达式一起使用(需要更长的时间才能正确)。同时:在Perl中,您可以构建一个自修改正则表达式。我将把它作为一个学术例子;-)
my @stuff = ( '"pattern"', 'pattern', 'pattern"', '"pattern' );
foreach (@stuff) {
print "$_ OK\n" if /^
(")?
\w+
(??{defined $1 ? '"' : ''})
$
/x
}
结果:
"pattern" OK
pattern OK
一般来说@Daniel Vandersluis的回复会起作用。但是,如果可选组 (") 为空,某些编译器将无法识别该组,因此它们不会检测反向引用。
为了避免这个问题,更稳健的解决方案是:
/^("|)(pattern)\1$/
那么编译器将始终检测第一组。如果表达式中有一些前缀并且您想先捕获它,也可以修改此表达式:
/^(key)=("|)(value)\2$/
对于支持
?P<name>
命名组语法的正则表达式,以下 Python re
示例说明了如何使用它来确保结束引号与可选的开始引号匹配:
re.compile(r"""^
(?P<quote>['"]?) # optional quote
(?P<sha1>([0-9-a-f]{40}))\s+ # SHA-1 of commit-id
# ...
(?P=quote)$""", # match opening quote if it exists
re.VERBOSE)
注意末尾的
?P=quote
,它引用了开头的命名模式。这可确保 blah
、'blah'
和 "blah"
全部匹配,但 'blah"
不匹配。
在 Perl 中也可以完成同样的操作,但它对
(?P=NAME)
的支持不允许使用单引号作为分隔符。所以
echo \"blah\" | perl -ne 'm/(?P<quote>["x]?)(\w+)(?P=quote)/ and print $2;'
可以工作,但为了支持单引号,必须使用
\k{NAME}
来反向引用。参见曼·佩尔雷(1).