置換s///(第8章「もっと正規表現」続き)

今までは主に正規表現での検索についてやってきたけど、今回は検索した結果を置き換える置換s///に関して。

簡単な置換

置換には「s///」という機能(これは演算子?手続き?関数?構文????)を使うそうな。
次のようなスクリプトを実行すると、

my $str = "How I wonder what you are.\n";
$str ~= s/what/who/;
print $str;

以下のように表示される。http://ideone.com/ve8RB

How I wonder who you are.

正規表現を表すときは2個スラッシュで囲んだけど、

m/foo/

みたいなm//を使った書き方ができた。それに似てて置換の場合は、

s/foo/bar/

って書いて、正規表現を1個目と2個目のスラッシュで囲まれた部分で指定して、置換後の文字列を2個目と3個目のスラッシュで囲まれた部分で指定する。

特殊変数$&を使って置換

上記の簡単な例のように、置換後を固定で書くと柔軟さに欠ける。すでに 第7章「正規表現の基本」 - チキン煮込みチーズミックス4辛 で出てきた特殊変数$&を使うと、マッチした部分の文字列をとってこれるのでなんか便利そう。

特定の語句を探して、それを一重引用符で囲む例
my $str = "How I wonder what you are.\n";
$str =~ s/what/'$&'/;
print $str;

http://ideone.com/YTWwZ

特定の語句を探して、それを増殖させる例
my $str = "How I wonder what you are.\n";
$str =~ s/who|when|where|what|why|how/$& $& $&/gi;
print $str;

http://ideone.com/CPVR1

正規表現で検索して削除

置換後の文字列を空白にしてやればOK

my $str = "How I wonder what you are.\n";
$str =~ s/w\w+//g;
print $str;

上記のスクリプトでwで始まる単語(wonderとwhat)を消せたけど、余計なスペースが残った。http://ideone.com/9BZkL
そんな時は「スペース0個以上」も正規表現に含めてやるといい。http://ideone.com/PGrvy

my $str = "How I wonder what you are.\n";
$str =~ s/w\w+ *//g;
print $str;

特殊変数$1、$2、$3、...の利用

これもすでに出てきたけど、使えるそうだ。

my $str = "The price is 300yen. The distance is 120km.\n";
$str =~ s/((\d+)([A-Za-z]+))/$2<$3>/g;
print $str;

単位である「yen」と「km」を括弧で囲んでみた http://ideone.com/JRSbA

my $str = "How I wonder what you are.\n";
$str =~ s/(\w+) (\w+)\./$2 $1!/g;
print $str;

単位である文末の2語を入れ替えてビックリマークを付け加えてみた http://ideone.com/Dcphl

式を評価する修飾子/e

修飾子/eを使うと、置換後の部分をPerlの式として評価してくれるらしい。

my $str = "The price is 300yen. The distance is 120km.\n";
$str =~ s/(\d+)/$&*2/eg;
print $str;

数値を2倍してみたhttp://ideone.com/VXcBR

my $str = "The price is 300yen. The distance is 120km.\n";
$str =~ s/(\d+)/$&/eg;
print $str;

上記のような場合だと、修飾子/eがあってもなくても同じ結果だけど、じゃあ「評価」って何なんだろう?
修飾子/eあり:http://ideone.com/71JqQ 修飾子/eなし:http://ideone.com/yLj2N

関数の定義方法はまだやってないけど、関数が使えると出来ることがぐっと広がりそう。

メタ文字\b

"and" を "AND" に置換したかったけどミスった例。http://ideone.com/1RTav

my $str = "Loves plum cake and sugar candy.\n";
$str =~ s/and/AND/g;
print $str;

こういうときはメタ文字\bで、単語の境界を明示してやればOK。http://ideone.com/C6b2C

my $str = "Loves plum cake and sugar candy.\n";
$str =~ s/\band\b/AND/g;
print $str;

\bは

  • \wと\W
  • \Wと\w

の間にマッチするそうだ。

修飾子

1行として扱う/s

メタ文字.は改行文字\nにマッチしないけど、修飾子/sをつけるとマッチするようになる。http://ideone.com/XZbri

my $str = "abc123\ndef456123xdef";
if ($str =~ /123.def/s)
{
  print "$&\n";
}

修飾子/sをつけない場合は http://ideone.com/Q6YEa

複数行として扱う/m

修飾子/mを使うと、改行を区切り文字とした複数行としてパターンマッチできる。http://ideone.com/9eG1C

my $str = "point123\npoint456\npoint789.\npoint012";
while ($str =~ /\d+$/mg)
{
  print "$&\n";
}