• ベストアンサー

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

$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;のように行末に追加しても、次の行とのあいだに空間が生まれます。基本的なファイル書き込みの知識をお尋ねして申し訳ありませんが,どうかお教えください。

  • Perl
  • 回答数4
  • ありがとう数4

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

  • ベストアンサー
  • 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

  • ファイルから読み込んで配列へ

    PerlでCGIスクリプト(掲示板もどき)を作成しています。 配列の操作がうまくいかず、ご教授頂きたいと思います。 <やりたい事> 1.ブラウザ上のフォームからの入力を、カンマ区切りでテキストファイルに追記して保存。 2.そのテキストの一部を使ってHTMLで表を作る。 <実際の作業> 1.フォームからのデータはcgi-lib.plで連想配列として受け取り、unshiftでテキストファイルへ追記する。 &ReadParse; #フォームからデータを受け取る $o=$in{'name'}; $p=$in{'mail'}; $q=$in{'title'}; $r=$in{'comment'}; open(IN,"xxx.txt"); @tmp1=<IN>; close(IN); #1件1行として新規データを追加する unshift (@tmp1,"$oo" , "," , "$pp" , "," , "$qq" , "," , "$rr","\n"); open(OUT,">xxx.txt"); print OUT @tmp1; close(OUT); 2.xxx.txtのデータを読み込んで、1列目(name)と3列目(title)だけを使った表を作る。 データを読み込む時点でつまづき、先に進めません。 <テキストファイルの例> 1行目:a,b,c,d 2行目:e,f,g,h 3行目:i,j,k,l 以下同様 <症状> print @tmp1;とすると、a~lの全てのデータが表示される。 $tmp1[0];とすると、(a)のみ表示される。 $tmp1[1];とすると、カンマ(,)が表示される。 $tmp1[2];とすると、(b)が表示される。 <質問> 一つの行(配列)に対して2回繰り返す処理(nameとtitle)と、行単位で繰り返す処理(1行目、2行目…)を合わせればいいのだろうと思っていますが、どうすればいいのかわかりません。 気分的には↓こんな感じです。 foreach { "行の処理" foreach { "列の処理" } } そもそも、区切り文字であるカンマが、配列に格納されている時点で失敗しているのかな?とも思っています。 宜しくお願いします。 なお、不足している情報がありましたら補足いたします。

    • ベストアンサー
    • Perl
  • [python] 関数から配列を戻すには?

    グローバル変数(配列)の値を変えたいと思います。 ------------- line = [] def tmp_read(self, *out_line): f = open('tmp.txt') lines2 = f.readlines() # 1行毎にファイル終端まで全て読む(改行文字も含まれる) f.close() # lines2: リスト。要素は1行の文字列データ for line in lines2: print line, print *out_line = lines2 return *out_line tmp_read(self, *line) print line     ← 配列Lineの内容を確認する ------------- この「line」には、文字列を含む配列を入れる予定です。 lines2で得られた値を、戻せば出来そうな気もしますが巧くいきません。 どのように修正すれば酔いか、教えて頂けませんか。

  • foreach構文をwhile構文で実現したい。

    下記にありますforeach構文をwhile構文で実現しようと試みているのですが 何故か同じように実現できず、無限ループになっているような気がします。 どこに問題があるのか当方ではわからない為、どなたかご教授いただけませんでしょうか。 ------------------------------ $ cat list1.txt 01<>ああああ 01_01<>あAAA 01_02<>あBBB 01_03<>あCCC 01_04<>あDDD 02<>いいいい 02_01<>いAAA 02_02<>いBBB 02_03<>いCCC 02_04<>いDDD ------------------------------ ■成功版 open(IN, "<list1.txt"); @datas = <IN>; close(IN); open(OUT, ">date.txt"); foreach (@datas) { chomp; ($a, $b) = split(/<>/, $_);#$_は省略できます。 print OUT "'$a'=>'$b',\n"; } close(OUT); ■取組版 open(IN, "<list1.txt"); @datas2 = <IN>; close(IN); open(OUT, ">date2.txt"); while (@datas2) { s/^/'/; s/<>/'=>'/; s/$/',/; print OUT; } close(OUT);

    • ベストアンサー
    • Perl
  • Spliceを使った配列移動

    以前Spliceを使った配列の移動方法を教えていただきましたが、 open( FH, $file) or die "Can't open"; @list = <FH>; close(FH); push(@list,@list[$b-1]); #配列を最後列にコピー open (OUT,"> $file"); print OUT @list; close (OUT); とさせると、最後の配列を5列目に追加したいのですが4列目に追加されてしまい、困っています。 2番目の配列を新たに5列目にこうしたいのに 1コメントA コメントB 2あいうえお かきくけこ さしすせそ 3たちつてと なにぬねの はひふへほ 4らりるれろ まみむめも 5あいうえお かきくけこ さしすせそ 4列目の配列の後ろに追加されてしまう 1コメントA コメントB 2あいうえお かきくけこ さしすせそ 3たちつてと なにぬねの はひふへほ 4らりるれろ まみむめもあいうえお かきくけこ さしすせそ このようになってしまいます。 宜しければ対処法をお願いします。

    • ベストアンサー
    • Perl
  • perl 計算結果をファイルへ出力したい

    perl やり始めたばかりです。宜しくお願いします。 入力ファイル data.txt があるとします。 data.txt は、 123 456 789 333 555 777 以上のようなテキストファイルといたします。このファイルを 以下の様に100分の1にして出力したい。 1.23 4.56 7.89 3.33 5.55 7.77 と言うことで、この場で教えていただきました。それが、以下です。 #!/usr/bin/perl open(IN, "data.txt") or die ; @x = <IN>; close (IN); foreach $line (@x){ chomp($line); @elms = split(' ',$line); foreach $data (@elms){ print $data/100," "; } print "\n"; } おかげ様でこれはこれで上手く動きました。そこで、出力値をファイルに 書き込みたいのです。 もちろん、以下の様な方法でファイルに 書き込めるのは判っております。 計算プログラム.pl > outfile.txt しかし、上のプログラムをベースにファイルに書き込めないかと色々と 試してはみましたが、どうも上手く行きません。 どなたか教えて頂けないでしょうか? 宜しくお願い申し上げます。

    • ベストアンサー
    • Perl
  • 配列を用いた文字置換

    初投稿です。よろしくお願いいたします。 kana2phone_rule.txtにはア+aという情報が五十音で入っています。01.txt.chaには愛+アイという情報が入っています。 以下のプログラムは01.txt.chaのプラスの後の要素 (アイ)を音表記(ai)に変換するというものです。 しかし変換の作業が行われない(アイ)ままファイルを吐き出してしまいます。 文字コードはEUC、改行コードLFで試しても、Jperlを用いてShift-JIS、CR+LFで試してもだめでした。 どこに問題があるのでしょうか。 分かりましたらよろしくお願いいたします。 open(IN,"kana2phone_rule.txt"); $j = 0; while($b=<IN>){ chomp $b; @list = split(/\+/, "$b"); $kana[$j] = "$list[0]"; $yomi[$j] = "$list[1]"; $j++; } close(IN); open(IN, "01.txt.cha"); open(OUT, "> kekka.txt"); while($a=<IN>){ chomp $a; @list2 = split(/\+/, "$a"); for($i=0; $i<=258; $i++){ $list2[1] = ~s/$kana[$i]/$yomi[$i]/g; } print OUT "$a [$list2[0]] $list2[1]\n"; } close(IN); close(OUT);

    • ベストアンサー
    • Perl
  • 配列のデータから改行だけを取り除いて上書きしたい

    いつもお世話になっています。 CGIで現在下記のようなファイルがあります。 【sample.cgi】 1<>2<>3<>4<>5<> 6<>7<> これを下記のスクリプトで呼び出し配列に入れます。open(IN,"./sample.cgi"); @DATA = <IN>; close(IN); @data = split(/<>/,$DATA[0]); @N_DATA=(); unshift(@N_DATA,"$data[1]<>$data[2]<>$data[3]<>$data[4]<>$data[5]<>$data[6]<>$data[7]<>"); open(OUT,">./sample.cgi") or &error('書き込み失敗'); print OUT @N_DATA; close(OUT); しかし実行結果は改行が残ったままで、どうにか 1<>2<>3<>4<>5<>6<>7<> という形で出力したいのですがどのように変更を行えば宜しいでしょうか。 恐らく「unshift」の部分が行を追加していっていると思うのですが、printなどで出力するとsample.cgiの中身が空になってしまい・・・。 「読み込むデータに改行があった場合は改行を削除して読み込む」という動作をさせたいのですが・・・。 何卒よろしくお願い致します。

    • ベストアンサー
    • CGI
  • 配列の作業

    perlを勉強し始めて一月足らずのものです。 どう考えてプログラムを組めばいいのかわからなく。 ちょっと困っています。 #!usr/local/bin/perl $acdata{'id'} = 125; $acdata{'go'} = 'http://www55.'; if(open(IN,"test5.log")){ @log = <IN>; close IN; }else{ $point =1; $log = join ('<>',$acdata{'id'},$point,$acdata{'go'}); open (OUT,">test5.log"); print OUT "$log\n"; close OUT; } foreach (@log){ ($id,$point,$url) = split (/<>/,$_); if ($id == $acdata{'id'}){ $point++; $log = join ('<>',$acdata{'id'},$point,$acdata{'go'}); open (OUT,">test5.log"); print OUT $log; } ログファイルの中のIDナンバーで管理するのですが ファイル自体がなかったらファイルを作って新規データーを書き込む IDナンバーと%acdata{'id'}が一致したらポイントを加算して記憶させる。 そこまではできたのですが、すべてのIDとと%acdata{'id'}が 合わなかった場合、データーを追加で書き込むという作業が なかなか思うように行かず、何度となく書いては消してを繰り返しています。 foreachの中でコントロールできないのかなと思ったりもしましたが どんな風に考えたらいいのでしょう? よかったらご指導ください。

    • ベストアンサー
    • Perl
  • ファイルを読み込んで改行だけの行を取り除きたい

    こんばんは。 ファイルを読み込んで、改行だけの行を省いて、上書きしたいのですが なぜかうまくいきません。 ループ内にある改行の判定をするIF文がいけないと思うのですが…。 教えていただけないでしょうか? また、シングルクォーテーションダブルっクォーテーションは同じ意味 なのですか?「""」と「''」 作ったプログラムは以下です。 open(OUT,">$ssifile"); #改行のみのデータは除く @newtbl = (); foreach $siline ( @slines) { if ($slines ne "\n") { push @newtbl, $siline; } } print OUT @newtbl; close(OUT); 宜しくお願いいたします。

    • ベストアンサー
    • CGI
  • 1ファイルずつ読み込みたい

    ウィンドウズ環境でactive perlを利用しています。 あるディレクトリーの中に2000位のファイルが入っており、このファイルを読み込んで置換処理を行うため下記のようなperlを作っています。 1ファイルはおおよそ3000行くらいです。 foreach(<*.html>) { open(IN_FILE, $_) || die; open(OUT, ">$_.tmp") || die; @record=<IN_FILE>; foreach $record (@record){ $record =~s/aaa/bbb/isg; } print OUT @record; close( IN_FILE); close( OUT); rename("$_.tmp", $_) ; } 当たり前かもわかりませんが、メモリー不足になり処理が中断します。 ディレクトリーの中のファイル数を600程度に少なくすると処理が完了します。 そこで質問なのですが、ディレクトリーの中のファィルを一度に全部読み込まず、1ファイルずつ順次読み込んでいくことはできないでしょうか。 最初の1行目のforeachをwhileに変えたりしたのですがうまくいきません。 対応策あればご教示ください。

    • ベストアンサー
    • Perl

専門家に質問してみよう