正则表达式因使用双引号和单引号而混淆

Regular Expression confused by use of double and single quotes

提问人:Charles Anderson 提问时间:2/5/2016 更新时间:2/14/2016 访问量:70

问:

我有这个JavaScript(在Chrome 48.0.2564.103 m中运行):

var s1 = 'label1="abc" label2=\'def\' ';
var s2 = 'label1="abc" label2=\'def\' label3="ghi"';
var re = /\b(\w+)\b=(['"]).*?def.*?\2/;

re.exec(s1); // --> ["label2='def'", "label2", "'"]
re.exec(s2); // --> ["label1="abc" label2='def' label3="", "label1", """]

第一个 exec() 与 label2 匹配,正如我所希望的那样。但是,第二个被“label3=”后面的双引号混淆,而是匹配 label1。

我本来以为会使用 .*?告诉正则表达式使匹配尽可能紧密,但显然并非总是如此。有没有办法收紧我的正则表达式?

JavaScript的 正则表达式

评论

0赞 Tushar 2/5/2016
一个简单的选项可以是 \w+=\S*def\S*

答:

3赞 Paul Chen 2/5/2016 #1

只需排除被视为报价的内容即可

/\b(\w+)\b=(['"])(?:.(?!\2))*def(?:.(?!\2))*.?\2/

所以变化是用 ..*?(?:.(?!\2))*

㧪:

  • (?!)是消极的展望未来,不捕捉
  • (?:)是非捕获组。
  • 如果不是,则结束报价之前的最后一个字母将不匹配,需要修复def.?

这允许您在想要允许或进一步时组合其他规则:a='\''a="\""a="\\\""

/\b(\w+)\b=(['"])(?:\\\\|\\\2|.(?!\2))*def(?:\\\\|\\\2|.(?!\2))*.?\2/

评论

0赞 Paul Chen 2/5/2016
@TimPietzcker 你编辑了我后面的部分,其实就足够了,因为展望未来是..*?\2
3赞 Hubro 2/5/2016 #2

给出不同结果的原因是您在 label2 之后的“def”右侧添加一个,这允许模式正确匹配字符串中第一个和最后一个双引号之间的所有内容。s2"

我只能猜测稀疏匹配 () 没有任何影响的原因是此时正则表达式引擎已经决定匹配而不是 .毕竟,正则表达式是从左到右做事的。?"'

解决此问题的“最简单”方法是仅匹配非引号,而不是在引号之间使用 :.

var re = /\b(\w+)\b=(['"])[^'"]*def[^'"]*\2/;

re.exec(s1); // --> ["label2='def'", "label2", "'"]
re.exec(s2); // --> ["label2='def'", "label2", "'"]

这样做的问题是,现在你不能在值中放置任何类型的引号,即使它们是完全合法的:

// This won't match because of the " after def
var s2 = 'label1="abc" label2=\'def"\' label3="ghi"'

// This won't match because there's an escaped single quote in the value
var s2 = 'label1="abc" label2=\'def\\\'\' label3="ghi"'

但基本上,正则表达式不是为解析 HTML 而设计的,所以如果这些限制是一个问题,你应该考虑正确的解析。

评论

0赞 Hubro 2/5/2016
注意:Diryboy 建议的正则表达式虽然看起来不同,但似乎与我的几乎相同,并且具有所有相同的限制。
0赞 Tim Pietzcker 2/5/2016
不,它还允许匹配,因为它不会排除匹配中的所有引号,而只是分隔引号。label1="abc'def'ghi"
0赞 Charles Anderson 2/5/2016
@Hubro:我之所以选择另一个答案,只是因为它是一个稍微完整的解决方案。但是,我可能会使用您的一个,因为它可以完成我接收的数据的工作。
0赞 Hubro 2/5/2016
@TimPietzcker 理论上我同意你的看法,但我测试了它,它不起作用。我猜 Chromium 47 中的正则表达式引擎用组 1 的模式而不是匹配替换了引用,使得:等效于此:\1(?:.(?!\2))(?:.(?!["']))
0赞 Paul Chen 2/14/2016
label1="abc'def'ghi"由于边界问题而不起作用,后面的查找与最后一个字母不匹配。我将更新答案以修复它。你可以试试这里 regex101.com/r/nC0gQ4/2i