优化充满'?'的正则表达式

时间:2017-03-24 21:33:14

标签: regex perl

在速记键盘上,有键STKPWHRAO*EUFRPBLGTSDZ。用户按下几个键,然后在提起时一次性注册键。它类似于在钢琴上弹奏和弦。示例笔划为KATTPHOEUGT

我有一个正则表达式,用于测试有效的steno和弦。它可以是任意数量的这些键,但它们必须按顺序排列。我的解决方案是qr/S?T?K?P?W?H?R?A?O?\*?E?U?F?R?P?B?L?G?T?S?D?Z?/,但由于此正则表达式被调用数百次,因此可变长度可能是速度瓶颈。由于所有?

,正则表达式中的每一步都是越来越大的可能性

对此有更快的正则表达式方法吗?如果密钥出现故障,我需要正则表达式失败。

2 个答案:

答案 0 :(得分:5)

要检查字符串是否是有效的和弦,您实际上需要

/^(?=.)S?T?K?P?W?H?R?A?O?\*?E?U?F?R?P?B?L?G?T?S?D?Z?\z/s

一个简单的优化就是确保匹配是可能的。

/^(?=[STKPWHRAO*EUFBLGDZ])S?T?K?P?W?H?R?A?O?\*?E?U?F?R?P?B?L?G?T?S?D?Z?\z/s

下一步是消除回溯。那是时间的流逝。

/
   ^
   (?=[STKPWHRAO*EUFBLGDZ])
   S?+ T?+ K?+ P?+ W?+ H?+ R?+ A?+ O?+ \*?+ E?+
   U?+ F?+ R?+ P?+ B?+ L?+ G?+ T?+ S?+  D?+ Z?+
   \z
/x

幸运的是,即使STPR出现两次,也可以毫无困难地完全消除回溯。这实际上几乎没有匹配的时间。

如果即使这还不够快,下一步就是编写一个专门的C函数。启动正则表达式匹配引擎是昂贵的,并且可以通过简单的功能完全避免。

请注意,上述优化仅在模式不匹配时才有用。当模式匹配时,它们应该是中性的。另一方面,C函数即使在模式匹配时也会有所帮助。

基准:

use strict;
use warnings;
use feature qw( say );

use Benchmark qw( cmpthese );

my %tests = (
   orig => q{ $s =~ /^(?=.)S?T?K?P?W?H?R?A?O?\*?E?U?F?R?P?B?L?G?T?S?D?Z?\z/s},
   new  => q{ $s =~
      /
         ^
         (?=[STKPWHRAO*EUFBLGDZ])
         S?+ T?+ K?+ P?+ W?+ H?+ R?+ A?+ O?+ \*?+ E?+
         U?+ F?+ R?+ P?+ B?+ L?+ G?+ T?+ S?+  D?+ Z?+
         \z
      /x
   },
);

$_ = 'use strict; use warnings; our $s; ' . $_
   for values %tests;

{ say "Matching:";     local our $s = "STAODZ";   cmpthese(-3, \%tests); }
{ say "Not matching:"; local our $s = "STPRSTPR"; cmpthese(-3, \%tests); }

输出:

Matching:
         Rate  new orig
new  509020/s   -- -29%
orig 712274/s  40%   --
Not matching:
         Rate orig  new
orig 158758/s   -- -73%
new  579851/s 265%   --

这意味着什么 匹配从1.40μs减慢到1.96μs(在这种情况下),和 非匹配速度从6.30μs到1.72μs(在这种情况下)。

要检查字符串是否是一系列有效和弦,您只需要

/^[STKPWHRAO*EUFBLGDZ]+\z/

如果你想提取一个字符串中的所有和弦,我首先要提取下面匹配的序列,然后在提取的序列中找到和弦:

/([STKPWHRAO*EUFBLGDZ]+)/

答案 1 :(得分:0)

  

可变长度可能是速度瓶颈

你不应该那样工作

  • 首先,编写并调试您的程序

  • 然后,如果它的速度不够快, profile 你的程序找到瓶颈的地方

  • 然后优化瓶颈

为了善良,不要花费多少时间来猜测瓶颈在哪里并在代码完成之前优化它们,因为你很可能会发现你猜错了并且浪费了很多时间

在任何情况下,正则表达式引擎都是用C语言编写的,速度非常快。我非常怀疑你所写的短模式是否需要花费大量时间来测试

  

由于所有?

,正则表达式中的每一步都是越来越大的可能性

这也不是真的。在正则表达式中的每个点上只有一个要测试的字符。字符串中的下一个字符要么匹配,要么不匹配。两者都很好,正则表达式引擎只是进入模式的下一步。无论要匹配的字符串如何,匹配过程都将保持不变。