2012-03-13 22 views
3

私はperlサブルーチンの定義を開始するperlコードと一致するようにperlで正規表現を書いています。ここに私の正規表現はあります:Perlの正規表現は貪欲ではありません

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

$ regexは、サブルーチンを開始するコードにマッチします。私は$ 1のサブルーチンの名前と、$ 2のサブルーチン名と初期の中括弧の間の空白とコメントを取得しようとしています。それは私に問題を与えている2ドルです。

は、次のPerlコードを考えてみましょう:私は、文字列の中に、このPerlのコードを入れて、$の正規表現に対してそれを一致

my $x = 1; 

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

は、$ 2があるが、ない三行「#これはコメント3 \ nは」私が欲しいコメントの私は正規表現が貪欲にコメントの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. 
' 
+2

['PPI'](http://search.cpan.org/perldoc?PPI)も参照してください。 '$ subs = PPI :: Document-> new(\ $ code_string) - > find( 'PPI :: Statement :: Sub'); ... – mob

答えて

7

ルック。それは(\s*#.*\n)です。それだけでコメント行をキャプチャすることができます。複数のコメント行をキャプチャするには、アスタリスクが後に付いています。これはうまくいきます。複数のコメント行をキャプチャして、$2の前の値を置き換えるたびに、それぞれを$2に1つずつ入れます。正規表現が一致すると$2の最終値は最後のであり、これは最終的なコメント行であるキャプチャグループが一致したものです。のみ。それを修正するには、キャプチャグループの中にアスタリスクを置く必要があります。しかし、アスタリスクがすべてに適用されることを確認するために、カッコの別のセット(非キャプチャ、今度は)を置く必要があります。だから(\s*#.*\n)*の代わりに((?:\s*#.*\n)*)が必要です。

あなたの3番目の正規表現は、かっこ内に無意識に囲んで式の後ろに疑問符を置くことができるので、機能します。これにより、$2はすべてのコメントを一度にキャプチャし、$3は最終コメントのみをキャプチャしました。

あなたは正規表現をデバッグしているとき、あなたはすべてで使用している試合変数の値をプリントアウトしてください:あなたは$1は、サブルーチンの名前だけだったことを見ているだろう$1$2$3など$2は3番目のコメントにすぎませんでした。これは、最初の2つのコメントの間に正規表現がどのように飛び越えたのか不思議に思うかもしれません。捕捉グループが複数回一致するときに何が起こるかを発見する方向につながります。

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

+1

ありがとうございます。私はあなたが問題を解決したと思う。実際には、デバッグ中に$ 1、$ 2、...の値を出力していました。私はここに投稿したテストコードを最小限に抑えました。||||正規表現の中で一致する部分は '([a-zA-Z _] \ w *)'、アルファベット文字またはアンダースコアの後ろにゼロ英数字、アンダースコア、および数字。それらのどれも空白に一致しません。私はそれをテストしました。 –

+0

おっと、そうです。 –

1

問題は、デフォルトで\nは、文字列の一部ではないということです。正規表現は\nに一致しなくなります。あなたは複数行のためs修飾子を使用する必要が

は一致します。

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

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

+0

これは間違っています、' \ n'は部分です正規表現は一致し続けます。そうしないと、OPの式のどれも一致しません。 –

+0

はい、この正規表現は 's'とおそらく' m'修飾子を使って書かれたほうが良いかもしれませんが、それがなければそのまま一致します。これは問題ではありません。おかげさまで –

4

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

  • ホワイトスペースとコメント一致する部分がに今ある:

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

    これは、以下の変更を除いて、あなたの$re3と非常によく似ています。ここでは

    は、私はあなた正規表現書き換えるだろうかです非捕捉グループ

  • 正規表現のその部分を((...)+)?から((...)*)に変更しました。これは同等です。 のみ$2をキャプチャし、あなたの正規表現の一部で
+0

ありがとうございます。私は今それを見る。私がしたいことには、追加のかっこが必要だと思われます。 –

関連する問題