Perlの正規表現で繰り返し置換する方法
Perlの正規表現を使って文字列を繰り返し置換する方法を紹介します。
テキストの中のある文字列を一律変換するのではなく、マッチする文字列の一部を除外しなければいけないという条件つきです。
ネットで色々調べてみましたが、同じ解決策がみつけられなかったので自力で作ってみました。
1.問題点
次のように、右辺に含まれる「0」だけを「A」に変換する必要が生じました。
変更前
102030=012301230123
変更後
102030=A123A123A123
「=」以外の、左辺および右辺の文字数や「0」の出現位置は不定とします。
実際には1行ではなく、このような行が数十行にわたって記述されていますが、1行単位で処理すればいいのでここでは省略しています。
ちなみにネットで調べたところ、次のようなサンプルは数多くヒットしました。
例:テキストに含まれる「0」をすべて「A」に変更
変更前
012301230123
変更後
A123A123A123
この例であれば、次のような一括置換を行えば一撃です。
my $content = '012301230123';
$content =~ s/0/A/g;
が、この方法を使って
my $content = '102030=012301230123';
$content =~ s/0/A/g;
とすると、左辺も含めてすべての「0」が「A」に変換されてしまいます。
変更前
102030=012301230123
変更後
1A2A3A=A123A123A123
2.特定の文字列を複数回置換する
追記:@usualomaさんから回答いただきました。ありがとうございました!
@yujiro 効率はよくないですが、$content =~ s/0(?!.*=)/A/g でもできそうです。
— Taku AMANO (@usualoma) July 29, 2013
「(?!式)」は、後続の文字列が式に一致しなければマッチする拡張構文です。
この場合、後続の文字列が「=」に一致しない「0」に(複数回)マッチするということになります。勉強になりました。
以降は元エントリーの内容ですがそのまま残しておきます。
「=」で文字列を分割して右辺を一括置換した後結合するという方法もありますが、ここではwhileと組み合わせて実現してみました。
my $content = '102030=012301230123';
while ( $content=~ s/(.*=)(.*)0(.*)/$1$2A$3/ ) {}
これで変換後の値は、右辺のすべての「0」が「A」に変換されて、
102030=A123A123A123
となります。
このコードではまず最後尾の「0」を「A」に変換し、while文を使って左方向に向かって右辺のすべての「0」を「A」に変換していきます。
3.その他
ちなみにg修飾子を使って、
$content =~ s/(.*=)(.*)0(.*)/$1$2A$3/g;
でよいのでは?と思った方もいるかもしれませんが、上のコードでは最後に出現した「0」だけが「A」に置換されて、
102030=01230123A123
となってしまいます。
また、最短マッチを意味する「量指定子+?」を使った、
$content =~ s/(.*=)(.*?)0(.*)/$1$2A$3/g;
の場合は、最初に出現した「0」しか「A」に置換されません。
102030=A12301230123
ということで2項のようなコードにしています。
よりエレガントな解があると思いますので、質問の意味も含めてエントリーしてみました。
- Perlの正規表現で文字列マッチを繰り返し判定する方法
- Perlの正規表現で条件分岐する方法
- 正規表現の最短マッチと最長マッチについて
- Perlの正規表現で複数行にマッチさせる方法
- Perlの正規表現で制御文字を削除する