3

perlサブルーチンの定義を開始するperlコードに一致するように、perlで正規表現を書いています。これが私の正規表現です:

my $regex = '\s*sub\s+([a-zA-Z_]\w*)(\s*#.*\n)*\s*\{';

$regex は、サブルーチンを開始するコードに一致します。また、$1 のサブルーチンの名前と、サブルーチン名と $2 の最初の左中括弧の間の空白とコメントをキャプチャしようとしています。私に問題を与えているのは2ドルです。

次の perl コードを検討してください。

my $x = 1;

sub zz
# This is comment 1.
# This is comment 2.
# This is comment 3.
{
    $x = 2;
    return;
}

この perl コードを文字列に入れて $regex と照合すると、$2 は "# This is comment 3.\n" であり、必要な 3 行のコメントではありません。正規表現は 3 行すべてのコメントを貪欲に $2 に入れると思っていましたが、そうではないようです。

$regex が機能しない理由を理解し、単純な代替を設計したいと思います。以下のプログラムが示すように、動作するより複雑な置換 ($re3) があります。しかし、なぜ $regex が機能しないのかを理解することが重要だと思います。

use strict;
use English;

my $code_string = <<END_CODE;
my \$x = 1;

sub zz
# This is comment 1.
# This is comment 2.
# This is comment 3.
{
    \$x = 2;
    return;
}
END_CODE

my $re1 = '\s*sub\s+([a-zA-Z_]\w*)(\s*#.*\n)*\s*\{';
my $re2 = '\s*sub\s+([a-zA-Z_]\w*)(\s*#.*\n){0,}\s*\{';
my $re3 = '\s*sub\s+([a-zA-Z_]\w*)((\s*#.*\n)+)?\s*\{';

print "\$code_string is '$code_string'\n";
if  ($code_string =~ /$re1/) {print "For '$re1', \$2 is '$2'\n";}
if  ($code_string =~ /$re2/) {print "For '$re2', \$2 is '$2'\n";}
if  ($code_string =~ /$re3/) {print "For '$re3', \$2 is '$2'\n";}
exit 0;

__END__

上記の perl スクリプトの出力は次のとおりです。

$code_string is 'my $x = 1;

sub zz
# This is comment 1.
# This is comment 2.
# This is comment 3.
{
    $x = 2;
    return;
} # sub zz
'
For '\s*sub\s+([a-zA-Z_]\w*)(\s*#.*\n)*\s*\{', $2 is '# This is comment 3.
'
For '\s*sub\s+([a-zA-Z_]\w*)(\s*#.*\n){0,}\s*\{', $2 is '# This is comment 3.
'
For '\s*sub\s+([a-zA-Z_]\w*)((\s*#.*\n)+)?\s*\{', $2 is '
# This is comment 1.
# This is comment 2.
# This is comment 3.
'
4

3 に答える 3

7

をキャプチャする正規表現の部分だけを見てください$2。です(\s*#.*\n)。これ自体では、1 つのコメント行しか取得できません。複数のコメント行をキャプチャするために、その後にアスタリスクがあり、これは問題なく機能します。複数のコメント行をキャプチャし、それぞれを$2に 1 つずつ挿入し、そのたびに の前の値を置き換えます$2。したがって、$2正規表現が一致したときの最終的な値は、キャプチャ グループが最後に一致したもの、つまり最後のコメント行です。それだけ。これを修正するには、キャプチャ グループ内にアスタリスクを配置する必要があります。ただし、アスタリスクが全体に適用されるようにするために、別の括弧のセット (今回は非キャプチャ) を配置する必要があります。だから代わりに(\s*#.*\n)*が必要((?:\s*#.*\n)*)です。

3 番目の正規表現が機能するのは、無意識のうちに式全体を括弧で囲み、その後に疑問符を付けることができるためです。これにより$2、すべてのコメントが一度$3にキャプチャされ、最後のコメントのみがキャプチャされました。

正規表現をデバッグするときは、使用しているすべての一致変数の値を出力てください: これにより、最初のキャプチャ グループと 2 番目のキャプチャ グループの間に何もないときに正規表現が最初の 2 つのコメントをスキップしたことに疑問を抱くようになったかもしれません。$1$2$3$1$2

ちなみに、サブルーチン名の後の空白もキャプチャしているようです$1。これは意図的なものですか?(おっと、ニーモニックを台無しにして、\w「w for whitespace」だと思っていました。)

于 2012-03-13T20:02:50.193 に答える
4

キャプチャグループに繰り返しを追加すると、そのグループの最終一致のみがキャプチャされます。$regexこれが、最後のコメント行にのみ一致する理由です。

これが私があなたの正規表現を書き直す方法です:

my $regex = '\s*sub\s+([a-zA-Z_]\w*)((?:\s*#.*\n)*)\s*\{';

$re3これは、次の変更点を除いて、と非常によく似ています。

  • 空白とコメントの一致部分が非キャプチャグループになりました
  • 正規表現のその部分をからに変更し((...)+)?まし((...)*)た。
于 2012-03-13T19:59:09.950 に答える
1

問題は、デフォルト\nでは文字列の一部ではないことです。正規表現は で一致を停止し\nます。

s複数行の一致には修飾子を使用する必要があります。

if  ($code_string =~ /$re1/s) {print "For '$re1', \$2 is '$2'\n";}

s正規表現の後に注意してください。

于 2012-03-13T19:55:51.723 に答える