如何将引号分隔的字符串与正则表达式匹配?

Gra*_*row 31 regex perl

如果我试图将引号分隔的字符串与正则表达式匹配,则以下哪个是"更好"(其中"更好"意味着更高效且不太可能做出意外事情):

/"[^"]+"/ # match quote, then everything that's not a quote, then a quote
Run Code Online (Sandbox Code Playgroud)

要么

/".+?"/   # match quote, then *anything* (non-greedy), then a quote
Run Code Online (Sandbox Code Playgroud)

假设这个问题是空字符串(即"")不是问题.在我看来(没有正则表达式新手,但肯定没有专家),这些将是相同的.

更新:经过反思,我认为更改+字符*无论如何都会正确处理空字符串.

Jan*_*rts 39

你应该使用第一名,因为第二名是不好的做法.考虑到您之后的开发人员想要匹配后跟感叹号的字符串.他应该使用:

"[^"]*"!
Run Code Online (Sandbox Code Playgroud)

要么:

".*?"!
Run Code Online (Sandbox Code Playgroud)

当你有这个主题时会出现差异:

"one" "two"!
Run Code Online (Sandbox Code Playgroud)

第一个正则表达式匹配:

"two"!
Run Code Online (Sandbox Code Playgroud)

而第二个正则表达式匹配:

"one" "two"!
Run Code Online (Sandbox Code Playgroud)

始终尽可能具体.尽可能使用否定的字符类.

另一个区别是[^"]*可以跨越行,而.*除非您使用单行模式.[^"\n]*也排除了换行符.

至于回溯,第二个正则表达式会回溯它匹配的每个字符串中的每个字符.如果缺少结束引号,则两个正则表达式都将回溯整个文件.只有回溯的顺序不同.因此,从理论上讲,第一个正则表达式更快.在实践中,您不会注意到差异.


小智 15

更复杂,但它处理转义引号和转义反斜杠(转义反斜杠后跟引号不是问题)

/(["'])((\\{2})*|(.*?[^\\](\\{2})*))\1/
Run Code Online (Sandbox Code Playgroud)

示例:
  "hello \"world"匹配"hello \"world"
  "hello \\"world"匹配"hello \\"


Har*_*ord 10

我会建议:

([\"'])(?:\\\1|.)*?\1
Run Code Online (Sandbox Code Playgroud)

但只是因为它处理转义的引用字符并允许'和'都是引用字符.我还建议查看这篇文章深入探讨这个问题:

http://blog.stevenlevithan.com/archives/match-quoted-string

但是,除非您遇到严重的性能问题或无法确定嵌入式引号,否则请使用更简单,更易读的内容:

/".*?"/
Run Code Online (Sandbox Code Playgroud)

我必须承认,非贪婪的模式不是基本的Unix风格的"ed"正则表达式,但它们变得非常普遍.我仍然不习惯像(?:stuff)那样对运算符进行分组.


Leo*_*ans 6

我会说第二个更好,因为当终止"丢失时它会更快失败.第一个将回溯字符串,这是一个可能很昂贵的操作.如果你使用perl 5.10,那么另一种正则表达式就是/"[^"]++"/.它传达与版本1相同的含义,但与第二版一样快.