3

(Ruby 1.9.2では)ゼロまたは偶数のバックスラッシュが前に付いた特別なコード(%x)のすべてのインスタンスを見つけて置き換えるための最良の方法を見つけようとしています。

言い換えると、 :

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

文字列には複数のインスタンスが含まれる場合があります:「これは、2つの%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

私は2つの代替正規表現を想像することができます:

  1. コードのように、後読みアサーションを使用します。(後ろ向き-2)
  2. バックスラッシュの前に、もう1文字一致します。(別)

それ以外は、正規表現のマイナーな最適化しか見られません。「%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 に答える