雑文発散

«前の日記(2008-02-13) 最新 次の日記(2008-02-15)» 編集
過去の日記

2008-02-14 [長年日記]

[PHP][Perl][Ruby] PHP / Perl / Ruby での正規表現の挙動の違い #3

たかはしさんからツッコミを受け、るびまの正規表現関係の記述を読んでみると、Ruby に関しては「標準添付ライブラリ紹介 【第 12 回】 正規表現 (1)」にそのものズバリが書いてあった。

文字列先頭や文字列末尾の意味で「^」や「$」を使ってはいけません。Perl などの他の言語の正規表現とは意味が違うので気をつけてください。 たとえば、CGI の入力のチェックで間違って「^\d+$」のように使ってしまうと、「数字だけからなる文字列」を受け付けたつもりでも「数字のみの行」を含む文字列を受け付けることになってしまいます。
「\Z」も普通は使うことはないでしょう。「\A」とセットで文字列全体をチェックするのなら「\z」を使うべきです。「\Z」は「^」に対応する「$」のように「\A」に対応するものとして存在するだけで、普通は使うものではないと思います。行を意識して処理をしたいのなら、「\A」と「\Z」ではなく「^」と「$」が向いていることの方が多いはずです。

hnw さんと同じく、オレも \z と \Z の存在を知らなかったのが今回の(オレの)混乱の原因か。ということで、\z を使ったコードを試してみた。

まずは PHP 版。

#!/usr/bin/php
<?php
 
$array = array('1234',
               '1234' . "\n",
               '1234' . "\n" . '5678');
 
for ($i = 0; $i < count($array); ++$i) {
    if (preg_match('/^([0-9]+)\z/',$array[$i],$match)) {
        print "ok: " . $match[1] . "\n";
    } else {
        print "ng\n";
    }
}
?>

次に Perl 版。

#!/usr/bin/perl
 
@array = ('1234',
          '1234' . "\n",
          '1234' . "\n" . '5678');
 
for ($i = 0; $i < @array; $i++) {
    if ($array[$i] =~ /^([0-9]+)\z/) {
        print "ok: " . $1 . "\n";
    } else {
        print "ng\n";
    }
}

最後は Ruby 版。

#!/usr/bin/ruby
 
array = ['1234',
         '1234' + "\n",
         '1224' + "\n" + '5678'];
 
array.each { |tmp|
   if /\A([0-9]+)\z/ =~ tmp then
      print "ok: " + $1 + "\n"
   else
      print "ng\n"
   end
}

実行結果はコレ。

$ ./test.php
ok: 1234
ng
ng
$ ./test.pl 
ok: 1234
ng
ng
$ ./test.rb
ok: 1234
ng
ng

この結果を見ると、ようやく全ての言語で期待通りの結果になった。Ruby の場合は、PHP / Perl と同じ /^[0-9]+\z/ では、これがまた違う結果になるので、上記の通り /\A[0-9]+\z/ としている。PHP / Perl で使っていた正規表現を Ruby に移行しようとするときには、この辺に気をつけないといけなさそうだなー。

ところで、Ruby が Perl と似た表記での正規表現を採用したのに、この部分の動作を変えた理由はなんなのだろう? 何かメリットがあって Perl とは違う道を選んだのだと思うのだが、それがどんな時なのかが思いつかない。「すげー便利」って何かがあるんだろうか。。。