• ベストアンサー

同条件で連続grepすると2回目がHITしない

grepを使った下記のようなプログラムの出力結果が理解できません。 --------------------------- @arr=("a"); $str="a"; @buf=grep{m/$str/g} @arr; print "buf=[@buf]\n"; @buf=grep{m/$str/g} @arr; print "buf=[@buf]\n"; --------------------------- [出力結果] buf=[a] buf=[] --------------------------- なぜ連続で同じ条件でgrepすると2回目はHITしないのでしょうか? なお、さらに繰り返しても、 buf=[a] buf=[] buf=[a] buf=[]  :  : のように一回おきにHITします。 また条件を一回ごとに変えるともちろんうまくHITします。 中で何が起きているのか理解できないので、 わかる方いらっしゃいましたら教えていただけないでしょうか? perlはActivePerl-5.8.7です。 なお、実際当方のプログラム内ではループの中で$strが変わっていきますが、 たまたま同じ検索文字列が続いたときにHITしなかったため、 上の簡易プログラムで試してみたところです。

質問者が選んだベストアンサー

  • ベストアンサー
  • t-okura
  • ベストアンサー率75% (253/335)
回答No.2

/g をつけるとマッチした位置から続けて検索が行われるため、 $arr[0] = "a" のときは、最初の grep では先頭の "a" に一致し、 二度目の grep では、文字列の末尾から検索するので一致しません。 最後まで検索したあと(検索結果が FALSE になったあと)は、再び文字列の 先頭から検索が開始されるため、三度目の grep では一致します。 また 最後まで検索しなくても /g で検索した時に一致した場所を 表す pos の値を変えることで検索開始位置を初期化することができます。 二つの grep の間に foreach my $arr (@arr) { pos($arr) = 0; } を入れて、検索開始位置を初期化すると、希望通りの結果となります。 ただ、例であげられたコードでは /g をつける必要はないように思いま す。

eprj
質問者

お礼

すばらしいです。 そういうことだったのですね。(目から鱗。。。 3度目にまた結果がうまく出ることも理解できました。 実際には@arrはCSVファイルを読み込んだものが格納されていて、 マッチングする行が複数あるため/gが必要だったのです。 posで検索位置を初期化することで希望通りの結果が得られました。 本当にありがとうございました。

すると、全ての回答が全文表示されます。

その他の回答 (2)

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

#前半部分はNo.2さんと本質的に同じだったから削除(^^;;; #せっかくかいたので,後半だけアップします. $a="xxxxx"; for (1..10){ $a=~m/x/g; print pos($a); } これの結果をみてもらえるとわかると思いますが, $aを/gでマッチさせたときには 「マッチ位置」pos($a)が付随します. そして,マッチ失敗したら,クリア(undef)されます. #参照:perldoc -f pos posとかは,/gcや\Gなんかと合わせて使われますようですね

eprj
質問者

お礼

こちらもありがとうございました。 実際上記のサンプルを試してみましたが、 123451234 という結果でした。 posが1、2、...と変化していき、 6回目のループではFalseになるので出力がなくなるんですね。 とても理解しやすいです。 おかげで解決できました。 本当にありがとうございました。 これでこの質問はクローズさせていただきます。

すると、全ての回答が全文表示されます。
  • Ceren
  • ベストアンサー率49% (90/183)
回答No.1

手元でちょっと試してみました。 どうした加減でそうなるかはよく解りませんが、 正規表現につけている「g」オプションが原因のようです。

eprj
質問者

お礼

No.2のお礼欄にも書きましたが、/gオプションは必要だったんです。 でも/gオプションをつけるとポジションを意識しないといけないということにつながりました。 ご検討いただきありがとうございました。

すると、全ての回答が全文表示されます。

関連するQ&A