第8章「もっと正規表現」

もっと正規表現をやるそうな。

マッチした範囲を複数個取り出す$1、$2、$3

my $str = '168,57,37';
if ($str =~ /(\d+),(\d+),(\d+)/)
{
    my $height = $1;
    my $weight = $2;
    my $age = $3;
    print "\$height = $height\n";
    print "\$weight = $weight\n";
    print "\$age    = $age\n";
}

正規表現内で3ヶ所カッコで囲んでいるけどこれがポイント。マッチした部分がそれぞれ自動的に$1と$2と$3を束縛するそうだ。
上記の例ではカッコの位置と変数を束縛する値の対応が直感的だったけど、一般的には、カッコ(コッカではなく)の順で、$1,$2,...と続くそうだ。
$1, $2, $3, ..., $n の、nはどこまで用意されてるのかな?

修飾子/xで正規表現を読みやすくできる

例えば

/((\d+)([A-Za-z]+))/

という正規表現は、修飾子/xを付けることにより

/(              # 1つ目の左カッコ。$1に設定されるのはここから
  (             # 2つ目の左カッコ。$2に設定されるのはここから
    \d          # 数字
    +           # 1回以上の繰り返し
  )             # $2に設定されるのはここまで
  (             # 3つ目の左カッコ。$3に設定されるのはここから
    [A-Za-z]    # 英文字
    +           # 1回以上の繰り返し
  )             # $3に設定されるのはここまで
)               # $1に設定されるのはここまで
/x

のように書けるらしい。コメント書けるし、スペースも自由に使える。でもスペースにマッチさせたい時困るよね...どうすんだろ。

/gによるマッチとwhile文

修飾子/gをつけなければ、文字列を先頭から末尾にスキャンしつつ、最初にマッチしたところで検索をやめてしまう。例えば

my $str = 'The price is 300yen. The distance is 120km.';
if ($str =~ /(\d+)([a-zA-Z]+)/)
{
  print "$&\n";
}

は300yenにマッチするけど、120kmにはマッチしない。http://ideone.com/KU3bq
だけど、

while ($str =~ /(\d+)([a-zA-Z]+)/g)
{
  print "$&\n";
}

みたいに修飾子/gをつけてwhileとともに用いると、マッチする部分を列挙できる。http://ideone.com/A81Xv

$1と\1

'see'や'too'など、子音のあとに同じ母音が2回続くパターンを見つけたい時、

/[bcdfghjklmnpqrstvwxyz][aeiou][aeiou]/

という正規表現では、'sea'や'toe'にもマッチしてしまうので、必要十分ではない。http://ideone.com/EtNxj
「同じ母音が2回続く」ということを表現する仕組みが必要だ。そのために、以下のような記法がある。

/[bcdfghjklmnpqrstvwxyz]([aeiou])\1/

\1というのが、カッコで囲んだ部分がマッチしたものの繰り返しであることを示している。http://ideone.com/dFLo5

変数を使って正規表現を読みやすく

今まで正規表現リテラルとして(?)書いていたけど、文字列で正規表現のオブジェクト(?)を作れる。

my $cons = '[bcdfghjklmnpqrstvwxyz]';
my $voel = '[aeiou]';
if ('see' =~ /$cons($voel)\1/)
{
  print "$&\n";
}

http://ideone.com/KJ5yY

デフォルト変数$_とパターンマッチ

foreachとwhileのデフォルト変数$_は既にでてきたけど、foreachの他にも出てくる場面があるそうだ。

while (<STDIN>)
{
  if (/^From:/)
  {
    print;
  }
}

上記のスクリプトには変数が1個も出てきてないけど、次のスクリプトと等価らしい。

while (my $line = <STDIN>)
{
  if ($line =~ /^From:/)
  {
    print $line;
  }
}

ポイントは3つ。

  • whileの条件式は$_に代入される
  • ifの条件式に正規表現だけを書くと(演算子=~を使わないと)、$_と正規表現演算子=~を実行するのと同じ
  • printに引数を与えなければ、$_が引数になる

正規表現を読みやすく

正規表現を読みやすくする方法はいくつかあって

  • 修飾子/xを使う
  • 変数を使う

の2つの方法は既に書いた。もう2通り紹介してあった。

正規表現を表す//は実は m//の省略

URLにはスラッシュやドットが含まれるのでURLの正規表現を作ろうとしたら、バックスラッシュでエスケープしないといけないので読みにくい。そういう場合はスラッシュで囲むのではなく、以下のように正規表現を書くといい。(注:http://www.hyuki.com/ は著者である結城浩先生のwebページ)

# 縦棒(|)で
m|http://www\.hyuki\.com/pb/|
# シャープで
m#http://www\.hyuki\.com/pb/#
# エクスクラメーションマークで
m!http://www\.hyuki\.com/pb/!
# カッコとコッカで
m(http://www\.hyuki\.com/pb/)
# 中カッコと中コッカで
m{http://www\.hyuki\.com/pb/}

http://ideone.com/RNnje

メタ文字\Qと\E

\Qと\Eに囲まれた部分では、メタ文字はその文字そのものを表すようになる(つまり、メタ文字として働かなくなる)。

m|\Qhttp://www.hyuki.com/pb/\E|

http://ideone.com/bwVJv