• ベストアンサー

foreachの制御

ファイルを編集するのにforeachで@allbodyを1行ずつ調べているとします。  foreach $line (@allbody){ $iを含む行があれば$tmpと入れ替えてファイルへ保存   if ($line =~ /$i/){    $line =~ s/.*/$tmp;    print FILE @allbody;   } でも$iを含む行がなければ、新しく追加書き込みをしたい で、上に続けて    else {     push (@allbody,$tmp);     print FILE @allbody;    }   } とやると、おかしい事なりますよねぇ どうすればいいのでしょう?

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

  • ベストアンサー
  • leaz024
  • ベストアンサー率75% (398/526)
回答No.2

「$iを含む行がなければ」というのは、@allbody 全体を調べた後でなければ判断できませんよね。なのに、それが foreach のブロック内にあるのは間違いです。 こういう場合は「$iを含む行があったか」というフラグ用の変数をループ前に用意しておき、ループ内で条件が満たされればフラグを立て、ループ後にそれをチェックする、という方法を取ります。 また、foreach で1行ずつ調べているのに、「print FILE @allbody;」で全行書き出すのもおかしいですね。1行ずつ見ているのだから「print FILE $line;」のようにその行だけを出力するか、最後に @allbody を1度に出力すべきです。 以上をまとめるとこんな感じ。 $changed = 0;  # $tmp との入れ替えフラグ foreach $line (@allbody) {   if ($line =~ /$i/) {     $line =~ s/.*/$tmp/;  # @allbody の内容にも反映されます。     $changed = 1;   } } if (!$changed) {   push @allbody, $tmp; } print FILE @allbody; ※コード内に全角空白を使っているので、コピーする場合はタブ等に置き換えてください。 なお、データ形式については書かれていませんが、恐らく @allbody の各行の末尾には改行が入っていることと思います。その場合、$tmp との入れ替えと追加で下記のような違いが生じてしまいます。 ・push(@allbody,$tmp);  # @allbody に「$tmp」が追加される。 ・$line =~ s/.*/$tmp/;  # その行の内容が「$tmp\n」となる。 これは正規表現の「.」が(通常は)改行にマッチしないため、「s/.*/$tmp/」では改行以外の部分を $tmp に置き換えることになってしまうからです。ですので、そこは置換ではなく、「$line = $tmp;」のように代入に変えた方が良いでしょう。 ※上記は、$tmp が末尾に改行を含んでいる場合です。含んでいない場合は、置換は「$line = "$tmp\n";」、追加は「push(@allbody,"$tmp\n");」としてください。 蛇足ついでに書きますが、もしデータ量が多い場合は「$iを含んでいるなら」という条件を、正規表現($line =~ /$i/)から文字列操作(index($line,$i) != -1)に変えるだけで圧倒的に速くなります。

Terari
質問者

お礼

アドバイスありがとうございます $i=0;とか$i=1;とかの意味は分かっていても使い方が今いち釈然としてなかってので、使った事なかったのですが、おかげでとても理解できました。とても便利なんで、これから使うようにします。 $tmpにすでに改行を含んであるので、$iを含む行を発見したらchompで改行を取り除いてから入れてましたが、そんな面倒な事しなくても素直に$line=$tmp;でよかったんですね‥‥なるほど‥‥ 色々とレベルアップできました! 例文(?)まで書いてもらって、ありがとうございました。とても助かりました。 低レベルですが、これからもまたちまちま頑張って行こうと思います。

その他の回答 (1)

  • tatsu99
  • ベストアンサー率52% (391/751)
回答No.1

やりたいことの意図が不明ですが、 ファイルへの書き込みは1回限りであるという前提で良いでしょうか。 とすれば、 $iを含む行があれば$tmpと入れ替えてファイルへ保存後、 終了するようにします。$iを含む行がなければ、foreachのループの後に制御が移りますので、そこで、ファイルへの書き込みをします。

Terari
質問者

お礼

回答ありがとうございます 説明力がなくてすみません‥‥くぅ でもこちらの意図は伝わっていたようで安心です で、foreachの中で2つとも処理しようとしていたのが間違いだという事で、なるほどとても納得しました! 助かりました。ありがとうございました。

関連するQ&A