電子書籍の厳選無料作品が豊富!

正規表現の質問です。
言語はPerlで組んでいます。

いくつかのhtmlファイルを順に開き、以下のようなコードを実行します。
$http{BODY}=~ /(?<=\[ No\.)([0-9]{$digit})(?= \/ $num)/o;

ここで
$http{BODY} 読み込んだhtmlファイル
$digit 変数。整数値。
$num 変数。整数値。
この正規表現で、
・・・・[ No.2 / 3 ]・・・・
といった感じの文字列から、この場合は「2」をマッチさせようとしました。

ところが最初に読み込んだファイルではうまくマッチしたのですが、2番目のファイルではマッチしません。

o オプションを外すとうまくいきました。o オプションは変数展開を1回行うとのことです。元のファイルはやたらとでかいので、o オプションを付けたら少しは早くなるかなと思いつけていたのですが。。。

ネットで調べると、
while( $s = <FH> ){
# 一度だけ展開する
if ( $s =~ /$arg/o; ){ .... }
このような用例で、$argは変数というよりも、セットされた文字列として評価されるとあります。でも前述の正規表現の2つの変数、$digitと$numは普通に値を書き換えられていましたけど。。。

それとも o オプションを付けた正規表現では、その正規表現を処理するためのメモリ領域に一度変数の値を取り込むと、二度と読み込むことをしないということでしょうか。だから変数の値が変わっても、正規表現が用いるメモリ領域の値は変化しない。。。

でも
$http{BODY}=~ /(?<=\[ No\.)([0-9]{$digit})/o;
だったらちゃんと$digitの値が変わったことに対応しているんです。他にもいっぱい o オプションを付けた正規表現を用いていますけど、全部正常に作動しています(バグに気づいていないだけかもしれませんけど)。

前述の正規表現とこれら正常に作動する正規表現の違いは、後者が変数1個であるのに対して、前者は2個であるということです。「変数展開を1回行う」の意味は、変数1個にしか対応しないという意味なのでしょうか。でも前者も最初のファイルだけなら2個の変数に対応しているのです。

どういうことなのでしょう。

A 回答 (2件)

oオプションは「変数展開を最初の1回だけ行う」です。



一度でも
$http{BODY}=~ /(?<=\[ No\.)([0-9]{$digit})(?= \/ $num)/o;
を実行すると、実行した時点の値で、正規表現が固定されます。

例えば、$degitが「1」、$numが「2」の状態で、上記の行を1回でも実行すると、その行は、それ以降
$http{BODY}=~ /(?<=\[ No\.)([0-9]{1})(?= \/ 2)/o;
として動作します。

2回目の実行前に、$digitや$numの値を変更しても、それは反映されません。これらがどんな値になってようが
$http{BODY}=~ /(?<=\[ No\.)([0-9]{1})(?= \/ 2)/o;
として動作します。

例えば
$digit=1
$num=1
while( $num < 10) {
  $http{BODY}=~ /(?<=\[ No\.)([0-9]{$digit})(?= \/ $num)/o;
  .....
  $num++;
}
とやっても
  $http{BODY}=~ /(?<=\[ No\.)([0-9]{1})(?= \/ 1)/o;
が9回行われるだけです。

怖いのは「ループカウンタとして$numは9まで増えて9回ループして終わるが、正規表現では、ずっと同じ値で処理される」って事です。

デバッグ表示で$numを表示したら、ちゃんと値は変化しているのに、値が変化しない正規表現が繰り返されるのです。

>ところが最初に読み込んだファイルではうまくマッチしたのですが、2番目のファイルではマッチしません。

当たり前です。

$numも$digitも「最初のファイルを読み込んだ時にセットされた値が展開されたあと、二度と評価されない」のですから、最初のファイルと同じ物しかマッチしません。

2番目のファイルを読み込んでも「最初のファイルで行った正規表現で実行する」ので、マッチする訳がありません。

>でも
>$http{BODY}=~ /(?<=\[ No\.)([0-9]{$digit})/o;
>だったらちゃんと$digitの値が変わったことに対応しているんです。

それは気のせいです。$digitが変化しても再評価されず、ずっと1回目の値で動いているけど、1回目も2回目以降も「偶然、マッチしてしまっただけ」です。
    • good
    • 0
この回答へのお礼

なるほど、件の正規表現から前方参照を削除した
$http{BODY}=~ /(?<=\[ No\.)([0-9]{$digit})/o;

の結果を見てみると、最初の$digitが1だと、他のファイルでも全て1桁の数字しかマッチしていませんでした。

うまく作動していると思われる o オプションは、全てs///の構文で用いたものでした。

もう一度調べなおすと、
s/Pattern/Replace/o
の構文では、Replaceにある変数は書き換えられるようですが、Patternにある変数は書き換えられませんでした。

ありがとうございました。

お礼日時:2011/11/15 19:07

Perl が言語として意図しているのは「それとも」の方です. つまり, //o は「『その正規表現』を最初に使うときのみ変数の値を展開する」ということを指示します.



たとえば
$m = 10;
while ($m > 0) {
$s = '1' x $m;
if ($s -~ /1{$m}/o) {
print "match\n";
} else {
print "unmatch\n";
}
--$m;
}
において o をつけたり消したりすると動作が変化します.
    • good
    • 0
この回答へのお礼

サンプルコードのおかげで理解しやすかったです。

ありがとうございました。

お礼日時:2011/11/15 19:09

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!