3

我试图找到最好的方法来查找和替换(在 Ruby 1.9.2 中)以零或偶数个反斜杠开头的特殊代码 (%x) 的所有实例。

换句话说, :

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

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

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

 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")  

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

4

1 回答 1

1

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

  1. 使用后视断言,就像在您的代码中一样。(向后看2)
  2. 在反斜杠之前再匹配一个字符。(选择)

除此之外,我只看到您的正则表达式的小优化。"%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”比不捕获它稍慢。

问候, 马蒂亚斯

于 2012-09-28T14:49:14.087 回答