• 締切済み

ループ内での後方参照の使用に関して

下記のようなプログラムを作成しました。 (Perl のバージョンは 5.8.8 となります。) for ($i=0; $i<=105; $i++) { $str1 = "str1"; $str2 = "1234567890str1test"; $str2=~ s/^(\d+)($str1)/test/g; print $i."\n"; print $1."\n"; print $2."\n"; print $str2."\n"; } このプログラムを実行しますと、ループが100回実施するまでは $1, $2の値を 取得できるのですが、100回を超えると取得できなくなります。 (上記で行っている置換処理は100回を超えても正常に処理されます。) 質問としましては、この現象は Perl のバグなのでしょうか。 それとも、私の正規表現の書き方に不備があるのでしょうか。 ネットでいろいろ調べてみたのですが、答えが見つからなかったため、 質問させていただきました。 よろしくお願い致します。

みんなの回答

  • kumoz
  • ベストアンサー率64% (120/185)
回答No.6

for ($i=0; $i<=105; $i++) { $str1 = "str1"; $str2 = "1234567890str1testtest"; $str2 =~ s/^(\d+)($str1)/test/g; print $i."\n"; print $1."\n"; print $2."\n"; print $str2."\n"; } 手元に v5.005_53 があるので試したところ、ループの1回目から $1, $2 は取得できないようです。^ と /g の併用は通常意味がないので、考えられていなかったのではないでしょうか。むろん、/mg とすると $1, $2 を取得することができます。 v5.8.x の段階は、直している (仕様変更?) 途中だとも考えることができると思います。質問者のように、気づく方がいるとは予想外でしょうが...。

回答No.5

また間違えた。 パターンAは4文字、パターンBは5文字残っています。 ↓ パターンAは4文字、パターンBは8文字残っています。

回答No.4

自分の回答をちょっと修正 > my $str1 = "str1"; > my $str2 = "1234567890str1testtest"; > $str2=~ s/^(\d+)($str1)/test/; s/^(\d+)($str1)/test/; ↓ s/^(\d+)($str1)/test/g; 3) "1234567890str1test"では$1と$2に値が入り、"1234567890str1testtest"では途中から入らなくなるか これもよくわかっていないので、参考程度にして下さい。 はじめの方への補足で > <誤> > $str2 = "1234567890str1test"; > ↓ > <正> > $str2 = "1234567890str1testtest"; とのことなので、なぜ2つで違いがでるかおもしろいです。 パターンA: "1234567890str1test" パターンB: "1234567890str1testtest" 二回目のマッチ対象文字列 パターンA: "test" パターンB: "testtest" パターンAは4文字、パターンBは5文字残っています。正規表現/\d+str1/は最低でも5文字必要です。したがってパターンAでは二回目のマッチをせずに終了し、パターンBでは二回目のマッチを(途中から)試行するようになるみたいです。 --- なお、質問者さんの現象は、perl v5.8.0では再現し、perl v5.10.1だと再現しませんでした。

eulazaemon
質問者

補足

_--_1l1_1_様 ご回答いただきありがとうございます。 _--_1l1_1_様がNo.2の2) なぜ途中から結果が変ったのかで記載されていた内容について そういう動きをするんだ。と驚きました。 perl v5.8.0と perl v5.10.1の間で、なにかあったのかもしれません。 リリースノートを確認してみたいと思います。 また、二回目のマッチ対象文字列 パターンA: "test" パターンB: "testtest" それぞれの動きの違いについて、_--_1l1_1_様が上記で記載されている 仕組みがあったんですね。

  • kabaokaba
  • ベストアンサー率51% (724/1416)
回答No.3

しばらく更新してないActivePerl 5.10.0だけど まったく問題なく動きます. NO.1さんと同じ意見です. ソースを簡略化する時点で何か間違った(実際$str2がちがってたようだし・・ けど,その違いは実行に影響がないと思う)のかも. バージョンをあげてみるとかやることはあると思う. もう一つ,ほとんど可能性はないとは思うけど どのOSだとかの実行環境を考えるとか, 意表ついて実は5.8.8の不具合の可能性もまったくないわけではないから 次のバージョン(5.8.9が5.8系の最後だと思う)リリースノートをみてみるとか. #あー5.14がでてるんだ・・一気に更新しようかな ソースを見る限り,おかしなところはないように思うし s///のgも問題ないはず. 一回目のマッチで$1,$2が定義されるけど それ以上マッチしないから,$1,$2が上書きされることはないです. あんまりdebuggerわからないけど use re 'debug' をつけて perl -dで実行させたら,正規表現のところで コンパイル時には Compiling REx "^(\d+)(str1)" synthetic stclass "ANYOF[0-9{unicode_all}]". Final program: 1: BOL (2) 2: OPEN1 (4) 4: PLUS (6) 5: DIGIT (0) 6: CLOSE1 (8) 8: OPEN2 (10) 10: EXACT <str1> (12) 12: CLOSE2 (14) 14: END (0) floating "str1" at 1..2147483647 (checking floating) stclass ANYOF[0-9{unicode_all}] anchored(BOL) minlen 5 でて,実行時には105回ほど Guessing start of match in sv for REx "^(\d+)(str1)" against "1234567890str" Found floating substr "str1" at offset 10... start_shift: 1 check_at: 10 s: 0 endpos: 10 Does not contradict STCLASS... Guessed: match at offset 0 Matching REx "^(\d+)(str1)" against "1234567890str1test" 0 <> <1234567890> | 1:BOL(2) 0 <> <1234567890> | 2:OPEN1(4) 0 <> <1234567890> | 4:PLUS(6) DIGIT can match 10 times out of 214748364 10 <67890> <str1test> | 6: CLOSE1(8) 10 <67890> <str1test> | 8: OPEN2(10) 10 <67890> <str1test> | 10: EXACT <str1>(12) 14 <7890str1> <test> | 12: CLOSE2(14) 14 <7890str1> <test> | 14: END(0) Match successful! Matching REx "^(\d+)(str1)" against "test" String too short [regexec_flags]... Match failed こんな風にでてくるから マッチは期待通りにできてるし,$1,$2の取得も期待通りできてます. だいたい想像できると思いますが BOL は ^ (Biginning Of Line) OPEN1 は最初のキャプチャの ( PLUS は + でCOLSE1が最初のキャプチャの ) です BOL(2)ってのは,正規表現の「二文字目」 /が1で^が2と数えてます. 私としては・・・むしろ NO.2氏が現象を再現できたのかなと思ってます. 再現できているなら,何か内部的な問題がある(あった)のかもしれません.

eulazaemon
質問者

補足

kabaokaba様 ご回答いただきありがとうございます。 デバッガを入れると、kabaokaba様が記載されているように 出力されるんですね。 詳しい解説ありがとうございます。 こちらの方でver5.10.0でやってみましたところ、正常に動作しました。 kabaokaba様が指摘されているように、ver5.8.8あたりにバグがあるのかもしれません。 リリースノートを確認してみたいと思います。

回答No.2

1) s/^(\d+)($str1)/test/g 実行後の$1と$2は不定 my $str1 = "str1"; my $str2 = "1234567890str1testtest"; $str2=~ s/^(\d+)($str1)/test/; これを実行するとgオプションがついているので 一回目のマッチ 対象 : "1234567890str1testtest" $1 = "1234567890" $2 = "str1" 結果 : 一致 二回目のマッチ 対象 : "testtest" $1 = ?? $2 = ?? 結果 : 不一致 したがって、s/^(\d+)($str1)/test/g 実行後の$1と$2は不定になります。試しにgオプションをはずしてみて下さい。上手くいきます。 2) なぜ途中から結果が変ったのか この例だと途中から処理の仕方が違うようです。すごい適当な解説な上、私もわかっていないので参考程度にして下さい。 前半 まず、"str1”が"1234567890str1testtest"の最後に含まれる場所をsubstrで調べます。これにより、、1) の二回目のマッチが実行する前から失敗するのがわかるので、二回目のマッチ自体をやろうとしなくなります。したがって、 $1 = "1234567890" $2 = "str1" が残ります。 後半 どうやら後半からは、substrで調べるのは無駄と判断したのか、やろうとしなくります。1) の二回目のマッチをやろうとするため、$1と$2が空になります。 $1 = undef $2 = undef

noname#158634
noname#158634
回答No.1

コードに問題はありません。 こちらでそのコードのみを走らせたところ、 105 1234567890 str1 testtest まできちんと出力されています。 よって原因はほかのところです。

eulazaemon
質問者

補足

tk-is-pg_1206 様 回答いただきありがとうございます。 回答いただいたあとの報告で申し訳ないのですが、 質問で記入しましたコードに間違いがありました。 箇所としては以下の部分です。 <誤> $str2 = "1234567890str1test"; ↓ <正> $str2 = "1234567890str1testtest"; 上記の<正>の場合でも、正常に動きますでしょうか。 また、回答いただいた内容で >原因はほかのところです。 に関しまして、考えられる原因としてはどのようなものがありますでしょうか。

関連するQ&A