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

时间:2008-12-17 16:19:55

标签: regex perl

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

/"[^"]+"/ # match quote, then everything that's not a quote, then a quote

/".+?"/   # match quote, then *anything* (non-greedy), then a quote

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

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

9 个答案:

答案 0 :(得分:34)

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

"[^"]*"!

或:

".*?"!

当您有主题时会出现差异:

"one" "two"!

第一个正则表达式匹配:

"two"!

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

"one" "two"!

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

另一个区别是[^“] *可以跨行,而。*除非你使用单行模式。[^”\ n] *也排除了换行符。

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

答案 1 :(得分:12)

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

/(["'])((\\{2})*|(.*?[^\\](\\{2})*))\1/

示例:
“hello \”world“匹配”hello \“world”
“hello \\”world“匹配”hello \\“

答案 2 :(得分:9)

我建议:

([\"'])(?:\\\1|.)*?\1

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

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

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

/".*?"/

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

答案 3 :(得分:5)

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

答案 4 :(得分:3)

我会选择第二,因为它更容易阅读。但是我仍然喜欢匹配空字符串,所以我会使用:

/".*?"/

答案 5 :(得分:1)

从性能角度来看(对于长字符串来说非常沉重,长时间运行的循环),我可以想象

"[^"]*"

".*?"

因为后者会对每一步进行额外的检查:偷看下一个字符。前者可以无意识地滚动字符串。

正如我所说,在现实世界的情景中,这几乎不会引人注意。因此,我会选择二号(如果我目前的正则表达式支持它,那就是),因为它更具可读性。否则就是第一名。

答案 6 :(得分:1)

当输入中的边界字符(在您的示例中为双引号)出现时,使用否定字符类可防止匹配。

你的例子#1:

/"[^"]+"/ # match quote, then everything that's not a quote, then a quote

只匹配最小的一对匹配的引号 - 非常好,大部分时间都是你需要的。但是,如果你有嵌套的引号,并且你对最大的匹配引号对(或所有匹配的引号)感兴趣,那么你的情况要复杂得多。

幸运的是Damian Conway准备好了救援:如果您发现有多个匹配的引号,那么Text::Balanced就在您身边。它还具有匹配其他配对标点符号的优点,例如,括号中。

答案 7 :(得分:0)

我更喜欢第一个正则表达式,但这肯定是一个品味问题。

第一个可能更有效率?

Search for double-quote
add double-quote to group
for each char:
    if double-quote:
        break
    add to group
add double-quote to group

对于涉及回溯的事情有点复杂吗?

答案 8 :(得分:0)

考虑到我甚至不知道“*?”事情直到今天,我已经使用正规表达20多年了,我投票支持第一个。它肯定会清楚你要做的是什么 - 你试图匹配一个不包含引号的字符串。