• ベストアンサー

配列の一部を書き換えてファイルに保存する方法は?

$dicname ='GermJ.txt'; open(IN, "$dicname"); @list = <IN>; close(IN); 読み込んだ配列@listの5番目のデータを'aaaaaaaaaa'に書き換えたいのですが、 $list[5]='aaaaaaaaaaaaaaaaa'; open(OUT, "> $dicname"); foreach (@list) { print OUT $_; } close(OUT); とやって、ファイルの中身を調べると、6番目の文字列データが5番目の文字列データの後ろにくっついてしまっています。 改行マークを $list[5]='aaaaaaaaaaaaaaaaa',\n;のように行末に追加しても、次の行とのあいだに空間が生まれます。基本的なファイル書き込みの知識をお尋ねして申し訳ありませんが,どうかお教えください。

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

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

テキストファイルを読み込むと、読み込んだデータには行末の改行データ(\n)が付いたままとなっています。 質問のように1要素を変更するだけなら、変更したデータの末尾に改行データをくっつければ済みますが、その方法は  $list[5] = 'aaaaaaaaaaaaaaaaa', \n; ではなく、  $list[5] = 'aaaaaaaaaaaaaaaaa' . "\n"; または  $list[5] = "aaaaaaaaaaaaaaaaa\n"; となります。 ※コンマやピリオドなど多少見にくい部分があるのでご注意ください。 ちなみに、もっと複雑な処理をしなければならない場合、いちいち "\n" をつけるのは面倒な上、バグの元にもなりやすいので、 ・読み込んだデータからは改行を削除 ・書き込みの際に改行を付加して出力 とすることにし、持ちまわるデータには改行が入っていないようにした方がよいでしょう。 質問のスクリプトで言うならば、  @list = <IN>; を  chomp(@list = <IN>);   # 一括で全行の改行を落とす。 とし、  print OUT $_; を  print OUT "$_\n"; とします。 この場合、$list[5]='aaaaaaaaaaaaaaaaa'; は変更する必要はありません。

tonka729
質問者

お礼

早速御教示を頂きありがとうございました。 試してみましたのでその結果をまずお知らせします。 $list[5]='aaaaaaaaaaaa'."\n"; とすると、結果は1行空白が入ってしまい、リストにいわば穴があきました。 つぎに、chomp(@list=<IN>);としてprint OUT "$_\n";と$list[5]='aaaaaaaaa';のくみあわせですが、やはり一行空白があきました。 私がやりたいことは、<textarea..>xxxx<Textarea>のなかの'aaaaaaaaa'に変更を加えてこの配列をまともとのファイルに書き込む、事です。 <Textarea>か、転送してCGIが受け取る段階で空白が入ったりするのでしょうか? ひどく低次元の質問ですが、端末上のプログラムでは考えられなかったことなので当惑してます。どうぞよろしくご指導をお願い申し上げます。

その他の回答 (3)

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

編集には TEXTAREA を使っているということでしたが、編集したテキストに改行が入ってしまった場合のCGI側での処理はできているのでしょうか? 編集時に改行を入力しなくても、HTMLのフォームが <textarea> abcde </textarea> となっていると、データは「abcde\n」となってしまいます。 ※これが <textarea> abcde</textarea> なら、改行は入りません。 この時問題になるのが改行の除去です。 クライアントが Windows だと改行は \x0D \x0A という2バイトでサーバに送信されますが、サーバが Linux で、かつ CGI での改行除去が「変数 =~ s/\n//g;」などだったりすると、\x0D が除去されず残ってしまいます。 \x0D は可視キャラクタではないので、ソフトによっては空白として表示されるかも知れません。 \x0D もきちんと除去するには、「変数 =~ tr/\x0D\x0A//d;」とします。(参考URLを参照) まとめて主要部のコードを示しますので、参考にしてみて下さい。 # 指定した1行($n)の編集フォームを表示する $n = 5; open(IN, "$dicname"); chomp(@list = <IN>);  # 改行がない状態にして close(IN); print <<__EOH__;  # 改行が入らないようにフォームを出力 <textarea name="xxx">$list[$n]</textarea> __EOH__ # 編集データ($str)で、'GermJ.txt' を更新する。 $n = 5; $str = 'aaaaaaaaaaaaaaaa';  # 改行削除済みとする。 open(IN, "$dicname"); chomp(@list = <IN>);  # ここでも改行がない状態にして close(IN); $list[$n] = $str;  # 改行削除済みデータに入れ替え open(OUT, "> $dicname"); foreach (@list) {   print OUT "$_\n";  # 改行を付加してファイルに出力 } close(OUT);

参考URL:
http://www.din.or.jp/~ohzaki/perl.htm#CRLF_Remove
tonka729
質問者

お礼

お示し頂いた参考URL にある=~ tr.. でやってみましたら、見事解決しました。なるほどです。とても参考になりました。chomp のこと、処理系により問題がありそうなこと、おぼろげに分かったような機がしました。ありがとうございました

回答No.3

4万行分だとどれだけのメモリを食うのだろうと 若干気になって仕方なかったりしますが(^^; 本題ですが、 データファイルの1行分をtextareaに読み込んで編集、 そのままその部分だけを書き換えようとしたところ、 以下のように無用な空白が入ってしまうということでしょうか? 3番目のデータ 4番目のデータ         ←意図しない空白 5番目のデータ どうにもならなかったら、 @list = grep {!/^$/} @list; で空白行は除いてしまうという裏技があります。 (無論、今回は使うべき場面ではありませんが(^^;) leaz024さんがおっしゃられてますとおり、 文末の\nの処理をしっかりやればきちんと処理できます。 ・textareaに表示する前にchomp ・書き込む際に最後に\n付加 すればOKだと思いますが、 もし空白行ができてしまうのであれば、 書き込む際に今回の該当部分だけ、 一個余計にchompしておく手もあります(当面の対策として)

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

てっきり「ファイル読み込み → 加工 → 元のファイルを更新」という話だと思ってたのですが、「ファイルの内容を変更するCGI」の話だった、という事でしょうか? とすると、私の経験上   編集者  Internet   CGI   'GermJ.txt'    |── access ─→|                 |← read ─|    |←─ form ───|    |─┐    |edit    |←┘    |── submit ─→|                 |─ write →|    |← complete ──| というような流れになっているのでは?と思うのですが、どうでしょうか? また、'GermJ.txt' を @list に読み込むと、$list[0] に1行目、$list[1] に2行目、という感じで データが入ると思いますが、「TEXTAREA で変更」と言う場合、それぞれの行を別の TEXTAREA で編集するのでしょうか?それとも、'GermJ.txt' の内容全体を1つの TEXTAREA で編集するのでしょうか? プログラムの全容が見えないので補足要求ばかりになってしまいましたが、よろしくお願いします。

tonka729
質問者

お礼

どうも、ありがとうございます。こちらこそ、説明がへたくそですみません。 GermJ.txt というファイルは、4万行のlinestringを書き込んであるものです。ですから、各行末は改行されています。この配列の1行を指定してTextareaに表示→ 編集→ 保存といきたいのです。  $list[5]の内容をTextarea上では'aaaaaaaaa'としたつもりで保存し、ファイルをあけて確認すると、'aaaaaaaaa( 空白 )'となっていて次行との関係が不正常になり、このため、画面上で順番に再度呼び出すとこの箇所で空行が現われるのです。  プログラムをお示ししたい思うのですが、必要な部分だけに絞り込むことがどうもできなくて、すみません。

関連するQ&A