• 締切済み

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

下記のようなプログラムを作成しました。 (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 のバグなのでしょうか。 それとも、私の正規表現の書き方に不備があるのでしょうか。 ネットでいろいろ調べてみたのですが、答えが見つからなかったため、 質問させていただきました。 よろしくお願い致します。

  • Perl
  • 回答数6
  • ありがとう数0

みんなの回答

  • 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

  • ループで置換がうまく行きません

    こんにちは。よろしくお願いします。 以下のようなソースで、 指定した文字列があれば置換、という作業を、 配列をループさせて連続して行いたいと思っているのですが、置換されません。 ---------------- $str = "年齢は<--age-->才"; #置換したい文字列の配列 $from[0] = '<--name-->'; $to[0] = 'やまだ'; $from[1] = '<--age-->'; $to[1] = '10'; $from[2] = '<--date-->'; $to[2] = '今日'; #配列分ループして、ヒットしたら置換する for ($i=0; $i<=2; $i++){ $str=~s/$from[$i]/$to[$i]/go; } print $str; --------------------- 結果:年齢は<--age-->才 --------------------- ところが、全く同じ処理をループせずに行うとうまくいきます。 ------------------- $i = 0; $str=~s/$from[$i]/$to[$i]/go; $i = 1; $str=~s/$from[$i]/$to[$i]/go; $i = 2; $str=~s/$from[$i]/$to[$i]/go; print $str; --------------------- 結果:年齢は10才 --------------------- こんな事ってあるのでしょうか? 何か間違いがありましたら教えて下さい よろしくお願いします

    • ベストアンサー
    • Perl
  • ループ中でのmy宣言と処理速度

    こんにちは、Perl入門者ですがよろしくお願いします。 質問内容はループ中にmy宣言をいちいちすると処理速度は落ちてしまうのかということです。 ソースを例に挙げると まず、$lenをループの外に書く方法と my $i; my $len; for( $i=0 ; $i<1024 ; $i++){ $len = length($str[$i]; } $lenをループの中に書く方法 my $i; for($i=0 ; $i<1024 ; $i++){ my $len; $len = length($str[$i]); } の2種類です(その他の変数については特に言及していません) 使いやすさでいえば、ループ内で宣言をした方が自動的に初期化されるのでうれしいのですが ふと、いちいちメモリの解放と確保を行っているわけですからOS(?)に負荷がかかって遅くなっているのではと考えました。 Perlで処理速度を測ることもできるようですが恥ずかしいながらスキル不足です。すいません。 どなたかご存知の方、自分なりの考えがある方はご教授頂きたく存じます。 よろしくお願いします。

    • ベストアンサー
    • Perl
  • strtokを使用したループ内でのstrtok

    お世話になっております。 現在C言語で下記のようなstrtokを使用したプログラムを作成しております。 (関係なさそうな箇所については省略しております。) 【ソース】 str="abc.def,ghi.jkl,mnopq.r,kkk.bbb" for( a=strtok(str,",\n") , a , a=strtok(NULL,",\n"){ b=strtok(a,"."); c=strtok(NULL,"."): } 【質問内容】 上記の内容を使用したソースでは、for文のa=strtok(NULL,",\n")にてaに値がはいりません。 (2回目のループに行かない) 上記のような処理を実施したい場合、皆様どのようなソースを作成しているかご教示いただけないでしょうか。 よろしくお願いいたします。

  • 変数をループ内で変更しループ外でも参照したい

    変数をループ内で変更しループ外でも参照したい Linuxのシェルを作成している最中にちょっとした壁にぶつかりました。 元々は以下のような感じの処理でした。 (A)------------------ FLAG=false awk "(NR>=2){print}" ${FILE} | while read LINE_STR do if […]; then FLAG=true fi done -------------------- 状況によってFLAGの値を変更し、あとの処理で FLAGの値に応じて異なる処理を行ないます。 で、少し調べたところパイプすると別プロセスになるので云々と あったのでループの前の定義でも中でも「export FLAG」と 書いてみたのですがダメでした。そういうもんじゃないのかと。 元々は、最初の1行は読み飛ばしたいという要望を持っていたので このような記述だったのですが、少し不本意ですが、 読み込むファイルの1行目も処理対象に含めることにした上で 以下のような記述に変更したところ一応動きました。 (B)------------------ while read LINE_STR do FLAG=true done < ${FILE} -------------------- 対処療法として今はこのようなコードにしましたが完全ではありません。 今自分の知識の中で実現可能な方法だと以下のような感じです。 ・フラグファイルを使用する ・1行読み飛ばした一時ファイルを作成しそれを使う ・読み込むファイルの仕様を変更し1行目のヘッダを削除する ・(B)の方法で読み込み、ループ内でカウンタを持ち、最初だけ  continueする どれでも一応実現は可能ですが、エレガントではありません。 そこで質問することにしました。 以下のどちらかもしくはそれ以外で私の希望を実現する方法を 教えてください。よろしくお願いします ・パイプを使用したループでループ内で変更した変数の値を取得する方法  ※(シェルの制約で出来ないのであれば、その旨を知りたいです) ・パイプを使用せず、1行読み飛ばす方法 ※そもそも1行読み飛ばす方法で 「awk "(NR>=2){print}" ${FILE}」 と書いていますが、これは妥当でしょうか? よりよい記述があればあわせて教えてください。 よろしくお願いします。

  • Forループの制御について

    VB 2005,Framework2.0を使用しています。 For文を使ったループについてお尋ねしたいことがあります。 For i As Integer = 0 To 10     ’処理 Next i とあったとします。 そうするとループ変数iが0から10になるまで連続してループを行うのですが、これをある条件の時に現在のループ変数から一つ飛ばして次のループからまた処理を行いたい場合どのようにすれば良いのでしょうか? 例えば0~10回中に、現在5回目で特定の条件が一致したときその次の6回目のループは飛ばして7回目のループから再開したいです。 ちなみにこの様に書いても0から10回必ずループされてしまいました。 Dim TEST(10) As Integer TEST(5) = 1 For i As Integer = 0 To 10 If TEST(i) = 1 Then i = i + 1 End If Next i

  • ループ処理を抜けた時点での処理回数を表示したい

    今、以下のような「green」が配列の何番目にあるのか調べるプログラムを考えています。 具体的な処理としては、配列の値を順番に調べて「green」を見つけたらループをぬけて何番目にあったかを表示する、といった処理になります。 ただ、現時点ではループをぬけた時何番目だったのかを取得できないでいます。 $numを取得して表示するにはどのような処理を行えばよいでしょうか。 解説していただけると幸いです。 ---プログラム--- class hoge{ function hoge(){ $this->color = array("red", "blue", "pink", "white", "black", "gold", "yellow"...,"green",...); } function test(){ for($i = 0;$i < count($this->color);$i++){ if($this->color[$i] == 'green'){ echo $this->color[$i]."\n"; break;###ここでループをぬける } } echo "緑は${num}番目"; } } $a = new hoge(); $a->test(); ---

    • ベストアンサー
    • PHP
  • select ループ

    セレクトBOXの中をループさせて表示したいのですが? 上手く表示されません。 エラーメッセージ ・webサイトがメンテナンス中 ・webサイトにプログラム上の問題が  あると出ます。 下記ソースでおかしいところあれば お教え願えませんでしょうか? <html> <head><title>session.html</title></head> <body> <?php print"<table border="1"><tr>"; print"<td>"; print"<select name=kosu>\n"; for ($i=0; $i<5; $i++){ print"<option value=$i>$i\n"; } print"</select>"; print"</td>"; print"<td><input type=submit value="登録"></td>"; print"</tr>"; print"</table>"; ?> </body> </html>

    • 締切済み
    • PHP
  • 関数の中のループについて

    typedef struct{ int num; char basic_gainen[MAX][32]; int particle[MAX]; }Gainen; int main (void){ Gainen g1, g2; char str1[256] = "外国_の_大型_の_船"; char str2[256] = "大型_の_船"; char buf[256]; divide(&g1, str1);//文字列の中から助詞と名詞を取得 divide(&g2,str2);//文字列の中から助詞と名詞を取得 printf("gainen:%s\n",print(&g1,buf)); printf("gainen:%s\n",print(&g2,buf)); if(hikaku(&g1,&g2)==1) printf("一部の単語は一致する\n"); } //二つの文字列を比較し、一部一致するかどうかの判定 int hikaku(Gainen *g1, Gainen *g2){ int n,i; if(g1->num != g2->num){ if(g1->num >= g2->num) n = g2->num; else n = g1->num; printf("n:%d\n",n); for(i=n;i>0;i--){ printf("inside loop i:%d\n",i); if(g1->particle[n] != g2->particle[n]) return 0; else if(strcmp(g1->basic_gainen[n],g2->basic_gainen[n]) != 0 ) return 0; } } return 1; } divide関数を省略させて頂きます。 hikaku関数のところで、二つの文字列の助詞と名詞が一致しなかったら0を返すその以外は1と返すというふうにしたいですが、実行したらhikaku関数から0の値wが返された。ループの数を表示したら、上のやり方でループがまわらないというのはわかったんです。上の条件判断はいけないですか?ご教授よろしくお願いします。

  • 正規表現で置換した後に前後も含めて空白(改行)のみの場合は削除する

    現在、Perlでテンプレート処理みたなものを自作しています。 例えば my $str = <<'TEST'; test $test $test2 test TEST my $test = 'test'; $str =~ s/(\$\w+)/eval($1)/eg; print $str; exit; のような感じの時に(実際には特定の記号の範囲内にてサブルーチンや Perl構文なども処理できるようにしていますがこの際には例外処理や 複雑な処理などは一切考慮しないものとします)置換結果が空白で 前後が改行などのみになった場合にその行を削除したいのですが どうしたらよいのでしょうか。 >>例(上記の場合の実行結果) ---------- test test test ---------- ↓(理想) ---------- test test test ----------

    • ベストアンサー
    • Perl
  • __DATA__の再利用

    こんばんは、皆さん。 以下のプログラムは1回しか__DATA__を読み込まないですが、 2回めのprintで__DATA__を表示させるにはどうしたらよいのでしょうか? ------------------------------------------------------ #!/usr/bin/perl $str = "DATA"; $aaa = &test($str); print $aaa; sub test($){ $instr = @_[0]; foreach (<$instr>){ print; } foreach (<$instr>){ print; } } __DATA__ 1 2 3 ------------------------------------------------------

    • ベストアンサー
    • Perl