【问题标题】:Why is a character class faster than alternation?为什么字符类比交替更快?
【发布时间】:2014-04-03 15:45:03
【问题描述】:

在如下示例中,使用字符类似乎比交替使用更快:
[abc] vs (a|b|c)
我听说有人推荐它,并使用 Time::HiRes 进行了简单测试,我验证了它(慢了约 10 倍)。
同样使用 (?:a|b|c) 以防捕获括号产生影响并不会改变结果。
但我不明白为什么。我认为这是因为回溯,但我在每个位置看到它的方式有 3 个字符比较,所以我不确定回溯如何影响交替。是因为执行的交替性质造成的吗?

【问题讨论】:

  • 想想引擎的逻辑 - 在第一种情况下,您告诉引擎您正在寻找 3 个字符中的一个。引擎可以优化。在第二种情况下,引擎必须检查第一个,失败,回溯,检查第二个,失败,回溯,最后检查最后一个选项。
  • 我已经运行了一些测试并使用pcretest 提取了一些调试信息。这是the results。很有趣的是,PCRE 以某种方式将[abc] 改进为[a-c],并且在使用捕获组时似乎有更多的“步骤”。
  • @BoristheSpider:我不确定这种差异。如果我们要查找 3 个字符中的 1 个,我们仍然需要测试 3 次。每个字符一个对吗?
  • 为什么要使用 linux 和 perl 标签?你只对 perl 解析正则表达式的方式感兴趣吗?我不知道任何作为 Linux 内核一部分的正则表达式解析。

标签: regex performance perl character-class regex-alternation


【解决方案1】:

这是因为交替之间的“OR”构造|回溯:如果第一个交替不匹配,则引擎必须在交替匹配期间指针位置移动之前返回, 继续匹配下一个交替;而字符类可以按顺序前进。在禁用优化的正则表达式引擎上查看此匹配:

Pattern: (r|f)at
Match string: carat

Pattern: [rf]at
Match string: carat


但简而言之, 引擎优化此(单个文字字符 -> 字符类)这一事实已经很好地暗示了交替效率低下。

【讨论】:

  • 很酷的图形,非常有见地!那些是怎么做的?
  • @AhmedFasih 嗨!图片是来自regex101.com 的正则表达式调试器功能的屏幕截图。您可以通过在栏中输入正则表达式,在测试字符串区域中输入测试字符串,然后在左侧边栏中选择“Regex Debugger”来使其工作。要获得屏幕截图那样的深色主题,请选择网页界面右上角的扳手图标,然后选择“深色主题”。
【解决方案2】:

因为像[abc] 这样的字符类是不可约的并且可以优化,而像(?:a|b|c) 这样的替代也可能是(?:aa(?!xx)|[^xba]*?|t(?=.[^t])t)

作者选择 not 来优化正则表达式编译器以检查替代的所有元素都是单个字符。

“检查下一个字符是否在这个字符类中”“检查字符串的其余部分是否匹配这些正则表达式中的任何一个”之间有很大的区别 em>。

【讨论】:

  • [abc] is irreducable 你不是说可约化的吗? IE。可以简化为更简单的东西吗?
  • @Jim:不,我的意思是字符类只是一组代码点,不能再简洁地描述了。
  • @Jim:如果您仍然不明白,请进一步探讨您的问题。
  • @Jim FYI:如果交替中的所有替代项都是常量字符串(即不是像环视这样的奇特的东西),它将被优化为 trie。因此,交替禁用任何优化是不正确的,只是 charclass 仍然比更通用的 trie 更有效,并且没有人费心通过添加单字母 trie → charclass 优化来进一步复杂化正则表达式引擎。要查看正则表达式编译到的操作码,请执行 perl -Mre=debug -E'qr/a|b|c/' 之类的操作
  • @amon:我讨厌你使用 “打扰”。考虑到大多数字符类看起来像[a-zA-Z][^"],我怀疑单字符串的尝试会更快。
猜你喜欢
  • 1970-01-01
  • 2011-06-21
  • 2012-02-09
  • 1970-01-01
  • 2011-05-01
  • 2016-12-26
  • 2022-03-30
  • 2020-12-18
  • 1970-01-01
相关资源
最近更新 更多