更有效的查找/替换非逃脱角色的方法

时间:2012-09-28 13:47:45

标签: ruby regex

我正在尝试找到最好的方法来查找和替换(在Ruby 1.9.2中)特殊代码(%x)的所有实例,前面是零或偶数个反斜杠

换句话说,:

%x      -->   FOO
\%x     -->   \%x
\\%x    -->   \\FOO
\\\%x   -->   \\\%x
\\\\%x  -->   \\\\FOO
etc.

字符串中可能有多个实例:“这是我的%x字符串,带有两个%x代码。”

在问herehere提出的问题的帮助下,我得到了以下代码来做我想要的事情:

 str.gsub(/
  (?<!\\)           # Not preceded by a single backslash
  ((?:\\\\)*)       # Eat up any sets of double backslashes - match group 1  
  (%x)              # Match the code itself - match group 2
  /x, 

  # Keep the double backslashes (match group 1) then put in the sub
  "\\1foo")  

然而,正则表达式似乎是重量级的。由于在我的应用程序中将以合理的频率调用此代码,因此我想确保我没有错过更好(更干净/更有效)的方法来执行此操作。

1 个答案:

答案 0 :(得分:1)

我可以想象两个可选的正则表达式:

  1. 使用后视断言,就像在代码中一样。 (查找后面-2)
  2. 在反斜杠之前匹配另一个字符。 (替代)
  3. 除此之外,我只看到正则表达式的小优化。 “%x”是常量,因此您不必捕获它。 (查找后面-1)

    我不确定其中哪一项实际上更有效率。因此,我创建了一个小基准:

    $ perl
    use strict;
    use warnings;
    use Benchmark qw(cmpthese);
    
    my $test = '%x \%x \\%x \\\%x \\\\%x \\\\\%x \\\\%x \\\%x \\%x \%x %x';
    
    cmpthese 1_000_000, {
        'look-behind-1' => sub { (my $t = $test) =~ s/(?<!\\)((?:\\\\)*)\%x/${1}foo/g },
        'look-behind-2' => sub { (my $t = $test) =~ s/(?<!\\)((?:\\\\)*)(\%x)/${1}foo/g },
        'alternative'   => sub { (my $t = $test) =~ s/((?:^|[^\\])(?:\\\\)*)\%x/${1}foo/g },
    };
    

    结果:

                      Rate   alternative look-behind-2 look-behind-1
    alternative   145349/s            --          -23%          -26%
    look-behind-2 188324/s           30%            --           -5%
    look-behind-1 197239/s           36%            5%            --
    

    正如您可以清楚地看到的那样,替代正则表达式远远落后于后视方法,并且捕获“%x”比不捕获它略慢。

    问候,马蒂亚斯