正規表現の最短マッチと最長マッチについて
正規表現の最短マッチと最長マッチについて紹介します。
1.問題点
次のようなHTML文書があると仮定します。
<div>aaa</div>
<div>bbb</div>
<div>ccc</div>
この文書からHTMLタグだけを取り除くために、Perlで次のように書いてみます。
#!/usr/bin/perl
use strict;
my $foo = <<EOF;
<div>aaa</div>
<div>bbb</div>
<div>ccc</div>
EOF
$foo =~ s/<.+>//g;
print $foo;
このスクリプトを実行すると、何も出力されません(厳密には改行文字が出力)。
2.最長マッチについて
1項で何も表示されない原因は、s///演算子による正規表現で「最長マッチ」を適用しているためです(赤色部分 )。
$foo =~ s/<.+>//g;
サンプルの「.+(1回以上の繰り返し)」、あるいは「.*(0回以上の繰り返し)」は最長マッチとなり、Perlであればデフォルトのパターンマッチになります。
つまり上記のように記述すると、各行頭の「<」と、マッチが可能な最後尾の文字、つまり行末の「>」にマッチします(下の赤色部分)。
<div>aaa</div>
<div>bbb</div>
<div>ccc</div>
3.最短マッチについて
サンプルで「aaa」などのテキスト部分のみを残すには、HTMLタグの「<」のあとで最初に出現する「>」、つまり最短マッチさせる必要があります。
最短マッチさせるには、次のように「?」を加えます。
$foo =~ s/<.+?>//g;
「?」は量指定子を示すものではなく、量指定子の直後に指定することで最短マッチを示す役割を果たします。
上記のように記述することで、以下の赤色部分がマッチするようになります。
<div>aaa</div>
<div>bbb</div>
<div>ccc</div>
修正したスクリプトは以下のとおりです。
#!/usr/bin/perl
use strict;
my $foo = <<EOF;
<div>aaa</div>
<div>bbb</div>
<div>ccc</div>
EOF
$foo =~ s/<.+?>//g;
print $foo;
これで次のような出力を得ることができます。
aaa
bbb
ccc
2014.2.18
説明に誤りがあったので修正しました。
- Perlの正規表現で文字列マッチを繰り返し判定する方法
- Perlの正規表現で条件分岐する方法
- Perlの正規表現で繰り返し置換する方法
- Perlの正規表現で複数行にマッチさせる方法
- Perlの正規表現で制御文字を削除する
「最長マッチについて」の説明が誤っています.
s/<.+>//g
の場合, '.'(ドット)は改行記号にはマッチしません.
そのため, 一番最後の '>'でなく, 各行の末尾の '>'に
マッチしています. その証拠にそのコードを実行したとき,
3つの改行が表示されるはずです.
説明のとおり最後の '&;gt'にマッチさせようとすると,
's'フラグをつける必要があります.
s/<.+>//sg
's'フラグは '.'を改行文字を含む任意の文字にマッチさせる
ためのフラグです. これで同じスクリプトを実行した場合,
改行が 1つしか表示されないことになります.
>syohexさん
ご返事遅くなりすいません。
ご指摘ありがとうございます。
おっしゃる通りドットは改行を含まない認識でしたが、推敲時に勘違いしてしまったようです。
記事は修正致しました。