前回の
正規表現にまつわる修飾子──マッチや置換方法の変更指示
(1)/g
修飾子も含む,
表2 正規表現演算子でよく使われる修飾子
修飾子 | 意味 | Perlの採用バージョン |
---|---|---|
/m | 複数行モード。^や$のマッチ方法を変える | - |
/s | 単一行モード。.が改行にもマッチする | - |
/i | 大文字/ | - |
/x | 空白を無視したうえでコメントを許可して正規表現を読みやすくする | - |
/p | マッチ文字列, | 5. |
/n | 丸括弧()でのキャプチャを抑制する | 5. |
/g | マッチを複数回行う | - |
/o | 正規表現の結果をキャッシュする | - |
/e | 置換文字列をPerlコードとして評価する | - |
/r | 置換を非破壊で行う | 5. |
※採用バージョンに-が入っている場合,
修飾子は複数指定可能で,/msi
などと列挙します。列挙順序は任意で,/ims
でも同義です。
以降では,
/m修飾子──冒頭や末尾のメタ文字を,改行文字の直後や直前にもマッチさせる
/m
修飾子は冒頭と末尾のメタ文字^
と$
のマッチ方法を変えます。この修飾子を指定した状態は複数行モード
/m
修飾子を指定しない場合,^
と$
は対象文字列の冒頭または末尾にマッチします。冒頭も末尾も1ヵ所ですので,/g
修飾子を指定しても,
/m
修飾子を指定した場合,^
と$
は対象文字列の冒頭または末尾だけでなく,/g
修飾子を合わせて指定すると,
言葉ではわかりづらいので,
my $str1 = <<END_STRING; ――(1)
Alice Smith
Bob Johnson
Carol Williams
Dave Brown
END_STRING
my $str2 = $str1; ――(2)
$str1 =~ s/^\s+//g; ――(3)
print "=== str1 ===\n", $str1;
$str2 =~ s/^\s+//gm; ――(4)
print "=== str2 ===\n", $str2;
$str1
に代入しています。$str1
のデータを$str2
にコピーしたあと,/m
指定なしの/m
指定ありの^
直後にある空白類文字\s+
を空文字で置換,
=== str1 ===
Alice Smith
Bob Johnson
Carol Williams
Dave Brown
=== str2 ===
Alice Smith
Bob Johnson
Carol Williams
Dave Brown
/m
なしの場合は文字列の冒頭Alice
の前にある空白のみが削除されますが,/m
ありの場合は冒頭以外に改行の直後,
末尾のメタ文字$
も同様に,/m
によって末尾だけでなく行末にもマッチします。
/m
修飾子の指定によって,^
と$
は厳密な冒頭や末尾の意味を失います。その代わり,/m
修飾子の有無にかかわらず冒頭にのみマッチする\A
,\Z
と\z
が用意されています/^\s+//
をs/\A\s+//
に置き換えた場合,Alice
の左側にある空白のみが削除されます。
- 注1)
- 対象文字列の末尾に改行がある場合に限り,
$
や\Z
は末尾だけでなく,その改行の直前にもマッチします。この挙動を望まない場合, 厳密に末尾にのみマッチするメタ文字 \z
を使います。
/s修飾子──ドットを改行文字にもマッチさせる
/s
修飾子は,.
を改行にもマッチするよう変更します。この修飾子を指定した状態は単一行モード
つまり,/s
修飾子を指定しない.
は
ドットが標準で改行にマッチしない理由
Perlの誕生以前からあるUNIXコマンドのgrep
やsed
などは,.
が改行文字にマッチするか否かは未定義だったとも言えます。これらUNIXコマンドでは,.*
を指定したとしても,
そのあと誕生したPerlなどの正規表現を備えたプログラミング言語では,.*
が行末を越えてマッチすることがないUNIXコマンドユーザーの直感を尊重するため,.
は改行にマッチしない方針を取りました。
/s修飾子の有無による違い
しかし,.
が改行にマッチしないと不都合であるため,/s
修飾子を用います。
実際の例を見てみましょう。
my $str1 = <<END_STRING; ――(1)
<a href="https://gihyo.jp/dp/">
Gihyo Digital Publishing
</a>
END_STRING
my $str2 = $str1; ――(2)
if ( $str1 =~ m|<a (.*?)>(.*?)</a>| ) { ――(3)
print "str1: リンクを発見しました\n";
print "属性一覧は $1\n内容は $2\n";
} else{
print "str1: リンクを発見できませんでした\n"; ――(4)
}
if ( $str2 =~ m|<a (.*?)>(.*?)</a>|s ) { ――(5)
print "str2: リンクを発見しました\n"; ┓
print "属性一覧は $1\n内容は $2\n"; ┛(6)
} else{
print "str2: リンクを発見できませんでした\n";
}
a
要素の属性一覧と内容にマッチするサンプルです。a
要素の内容に,
/s
修飾子がないため,.
が改行文字とマッチしません。よって
一方,/s
修飾子があるため,.
は改行文字にもマッチします。よって
str2: リンクを発見しました
属性一覧は href="https://gihyo.jp/dp/"
内容は
Gihyo Digital Publishing
/s
修飾子とドットを使用しない方法
メタ文字.
が修飾子/s
の有無によって意味を変えることが混乱につながるのであれば,■
以外の任意の1文字[^ ■ ]
を,.
の代わりに使う方法もあります。
my $str1 = <<END_STRING;
<a href="https://gihyo.jp/dp/">
Gihyo Digital Publishing
</a>
END_STRING
if ( $str1 =~ m|<a ([^>]*)>([^<]*)</a>| ) { ――(1)
print "str1: リンクを発見しました\n"; ┓
print "属性一覧は $1\n内容は $2\n"; ┛(2)
} else{
print "str1: リンクを発見できませんでした\n";
}
/s
修飾子もメタ文字.
も使用していないことに注目してください。[^>]
と[^<]
はそれぞれ>
と<
以外の任意の1文字を表す正規表現ですが,\n
も含まれます。よって,
この[^■]
で説明するなら,/s
修飾子が指定されていない場合の.
は[^\n]
と同等です。
マッチを期待する文字列によっては,.
を使わずに正規表現を組み立てるほうが,/s
修飾子の有無を気にすることなく読みやすい場合があります。