Perlのopen()で+<を使用した時の挙動が変

このQ&Aのポイント
  • open()で+<を使用すると読み書き両方ができると理解しましたが、書き込んだ後の結果が私が思っていた挙動と違っていました。
  • 例えば、open(FILE,'+<test.txt') or die "$!"; my $line=<FILE>; print FILE "ABC\n"; close(FILE); を実行すると、test.txtファイルの内容が予想とは異なっていました。
  • なぜこのような結果になるのでしょうか?
回答を見る
  • ベストアンサー

perlのopen()で+<を使用した時の挙動が変

open()で+<を使用すると読み書き両方ができると 理解しましたが、書き込んだ後の結果が 私が思っていた挙動と違っていました。 例えば、 ---test.pl-------------------------- open(FILE,'+<test.txt') or die "$!"; my $line=<FILE>; print FILE "ABC\n"; close(FILE); ---test.txt---- 123 456 789 --------------- を実行すると ---test.txt---- 123 ABC 789 --------------- となることを期待していましたが実際には ---test.txt---- 123 456 789 123 ABC ---------------- となっていました。 1行だけ読み出したときのファイルポインタ は2行目の先頭を指しているように思われますが なぜこのような結果になるのでしょうか。 (Windows7,ActivePerl)

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

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

  • ベストアンサー
  • chie65535
  • ベストアンサー率43% (8522/19371)
回答No.2

>なぜこのような結果になるのでしょうか。 「printする前にseekをしていないから」です。 Perlが「my $line=<FILE>;」を行なった状態では、内部的に「ストリームバッファに、ファイルのすべての中身を読み込んでいて、内部的なファイルポインタがファイル末尾まで進んだ状態」になっています。 そして「ストリーム入出力用のラインバッファ」には「読み込んだ『123\n』」が入っています。 この状態で「print FILE "ABC\n";」を実行すると「ストリーム入出力用のラインバッファ」に「ABC\n」が「追加」されます。そして、ストリーム入出力用のラインバッファの内容は「元々あった『123\n』に『ABC\n』が追加された状態」になります。 つまり、ストリーム入出力用のラインバッファの内容は「123\nABC\n」になります。 ここで「close FILE;」を行なうと「ストリーム入出力用のラインバッファの内容がフラッシュ」されます。つまり「実際に書き込み」されます。 書き込み位置は「内部的なファイルポインタがファイル末尾まで進んだ状態」ですから「ファイルの末尾」になります。 結果「ファイルの末尾に、123\nABC\nの2行を書き込む」と言う事になります。つまり、test.txtの内容が 123 456 789 123 ABC になります。 >となることを期待していましたが 期待通りの動作をさせる場合は「読み込みを行なった後、書き込みを行なう前に、seek関数を使用し、内部的なファイルポインタを正しい位置に移動すると共に、ラインバッファをクリアする必要」があります。 つまり open(FILE,'+< test.txt') or die "$!"; my $line=<FILE>; seek FILE,0,1; print FILE "ABC\n"; close(FILE); とする事で、期待通りになります。 「seek FILE,0,1;」は「ファイルポインタを現在位置から0だけ動かす」なので「ファイルポインタを移動させない」のですが、これは「内部的なファイルポインタを、ストリームの現在の表面的なファイルポインタに一致させ、ストリームバッファをクリアし、ラインバッファもクリアする」と言う効果があります。 このように、ストリームの処理を「読み込み処理から書き込み処理に変える時」は、必ず「seek関数を使う必要」があります。 これは「Perlだけ」に限った話ではありません。内部的にストリームをバッファリングしている処理系では、必ず、同様の事が起きます。CやC++、C#でも同様の事が起きます。

yam2012
質問者

お礼

なぜこのようなことが起きているのか、 不可思議な現象の的確な解説と、具体的な対処方法、 ファイルポインタを同じ位置に移動させるだけなのに seek FILE,0,1を実行しなければならない理由など、 私が知りたかったことすべてがここに説明されています。 とても分かりやすい説明ありがとうございました。

その他の回答 (1)

回答No.1

ファイルを読み書き両用でオープンしても、書き込みはファイルの最後から行われます。 ファイルの途中を書き換えることは出来ません。

yam2012
質問者

お礼

書き込みがファイルの最後から行われた場合は ---test.txt---- 123 456 789 ABC ---------------- となりますが123も書き込まれているのはなぜですか。

関連するQ&A

  • ファイルオープンの挙動

    windows環境にてperlの勉強をしております。 http://www.rfs.co.jp/sitebuilder/perl/05/03.html#open を参考にopenを使っておりました。 ファイル書き込みがうまくできていないようなので 以下のようなテストプログラムを作成したのですが 上記のサイトのような挙動をしませんでした。 --source--- #!/usr/local/bin/perl print "Content-type: text/html\n\n"; open(FILE, "+> sample.txt"); $data = <FILE>; print "data = $data<br>"; $flag = print FILE "TEST MESSAGE"; print "flag=$flag<br>"; close(FILE); --source-- --sample.txt-- 実験です --sample.txt-- windows環境だと data = flag= と表示され linux環境(レンタルサーバ)だと data = flag=1 と表示されます。 私の予想では data = 実験です flag = 1 と表示されると思ったのですが何が原因でしょうか。 またlinuxとwindowsで結果が違うのは何か原因があり、 また修正可能でしょうか。 openに+>の挙動例が書いていないサイトもあることが 若干気になります。 もし+>自体が不安定なのでしたら安定して使えるもの をおしえてください。所望の挙動は読み取り+上書き書き込みです。 環境 windows2000 "an http daemon" linux (infoseekフリースペース) apache

    • ベストアンサー
    • Perl
  • EOFの書き込み Perl

    CGI初心者です。質問させてもらいます。 テキストファイルを読み書きモードでオープンします。 open(FILE, "+<test.txt"); 読み込みや書き込み作業をした後、 現在のファイルポインタの位置や、 seek FILE, 0, 0; などファイルの先頭で、 EOFを書き込めないのでしょうか? 例)print FILE 'EOFの文字'; Cでは出来たような気がしましたが・・ ファイルポインタの位置でファイルを終わらせる 簡単な方法ありましたら、お教え願います。m(__)m

    • ベストアンサー
    • CGI
  • perl 5.8.8 日本語マッチ

    perl5.8.8を使っています。 日本語にマッチする正規表現を書きたいのですが、どうしてもマッチしません。 例えば、以下のファイルtest.txtから「さしすせそ」だけを抽出し、表示させたいです。 ---------test.txt-------------------------------- あいうえお かきくけこ さしすせそ たちつてと -------------------------------------------------- ----------test.pl-------------------------------- use strict; use warnings; open(FILE, 'test.txt') or die "$!"; my @file = <FILE>; close(FILE); foreach my $line (@file){ if($line =~ /^さ/){ print "$line\n"; } } ------------------------------------------------ このtest.plを実行しても「さしすせそ」を抽出することが できません。 どうしたらよいのでしょうか? 自宅の新しいバージョンのperlだとできるのですが 会社のperlは5.8.8で顧客環境でもあるのでバージョンアップも できません。 すみませんが、よろしくお願いいたします。

    • ベストアンサー
    • Perl
  • mac で perl  open関数について

    お世話になります。 現在、perlの勉強中です。 状況は mac osx 10.6.6です。 使用参考書は「perl言語プログラミングレッスン[入門編]」です。 CGIは関係なしで行っております。 open関数について参考書を読みながら、 勉強をしているのですが、openでファイルをオープンするときについてなのですが、 参考書上では、「関数openは、指定されたファイル名を持つファイルをハードディスクから探し出し、オープンしようと試みます」と言う説明があるのですが、 今現在、試している文例では、特定の場所にファイルがあるときでないと、 オープンしてくれません。 文例は [test.pl] use strict; use warnings; my $filename = 'file.txt'; open(FILE, $filename) or die "$filename: $!"; while (my $line = <FILE>){ $line =~ s/ /_/g; print $line; } close(FILE); という形で、ファイルの " " (スペース)を抜き出して "_" (アンダースコア)に置き換えるという設問です。 ただ、現況は                  /Users/(myname)/Documents/test/test.pl #(myname)は私の名前のフォルダです。 にあるコードをターミナル上で実行すると、 /Users/(myname)/file.txt に配置しているときしか、読み込んでくれません。 そこから、ファイルを移動させてしまうと、 読み込んでくれないのです。 そこで、ご質問ですが、 1 「関数openは、指定されたファイル名を持つファイルをハードディスクから探し出し、オープンしようと試みます」 と参考書に記載がありましたが、実際には探し出しはしてくれないのでしょうか? それとも、自分のパソコン、コードに問題があるのでしょうか? 2 自分が指定した場所にあるファイルをオープンするには どのようにすればよいでしょうか? ターミナルで指示するのか、それともコードに加えるべきか、 ご指示くださいませ。 また、詳しい方がおられましたら、 お答えいただければと思います。 それでは失礼します。

    • ベストアンサー
    • Perl
  • Perlのプログラミングについて

    Perlのプログラミングでつまづきました。 # ファイルから指定文字列を含む行を収集する # 入力ファイルのオープンと読み込み print( "入力ファイル名?" ); $n = <STDIN>; chomp( $n ); open( FIN, "<$n" ) or die "入力ファイルオープンエラー: $!\n"; $n = @a = <FIN>; close( FIN ); print( "$n 行読み込みました\n" ); # 行の収集 print( "検索文字列?" ); $x = <STDIN>; chomp( $x ); $ptn = $x; #指定の文字列 $x = @b = grep( /$ptn/, @a ); print( "$x 行見つかりました\n" ); # 出力ファイルのオープンと書き出し print( "出力ファイル名?" ); $y = <STDIN>; chomp( $y ); open( FOUT, ">$y" ) or die "出力ファイルオープンエラー: $!\n"; print FOUT ( $ptn, "\n" ); print FOUT ( $x, "\n" ); print FOUT ( @b ); close( FOUT ); というプログラムで実行すると C:\My Perl\pl>perl プログラムの実行.pl 入力ファイル名?sample1.txt 168 行読み込みました 検索文字列?k 45 行見つかりました 出力ファイル名?out3-24.txt 続行するには何かキーを押してください . . . となり出力ファイルの中身が表示されません。 どこを間違えているのかご指摘いただけないでしょうか?

  • Perl 先頭行に追加

    恐れ入ります。 Perlで、配列がカンマで区切られたデータを読み込み、 その先頭行にデータを挿入したいのですが、 中々うまくいきません。 データ(/data/xxx.txt)の中身は、 test,test2 bbb,bbb2 ccc,ccc2 という感じで、カンマで区切られて改行で並んでいます。 そこに新たに$dateと$contで受けたデータを先頭行に追加する感じです。 open(FILE,"<./data/xxx.txt"); while($temp = <FILE>){ @temp=split(/,/,$temp); push(@ddr,"$temp[0]","$temp[1]"); } seek(@ddr,0,0); unshift(@ddr,($date,$cont."\n")); open(FILE,">./data/xxx.txt"); $new=join(",",@ddr); print FILE $new; close(FILE); このように書いていますが、 print FILE @ddr とすると、配列が壊れた状態(全て繋がる)で先頭行には挿入されたのですが、カンマで区切って配列にして並べようとしたら、 test,test2 ,bbb,bbb2, ,,ccc,ccc2,, という感じで上手く並びません。 よろしくおねがいします。

    • ベストアンサー
    • Perl
  • ファイルの3行目までを出力したい

    Perl初心者です。 test.txtというファイルがあって、その中の1行目から3行目までを 出力したい場合はどうしたらいいでしょうか? open(FILE,"test.txt") || die "Open Error.\n"; @data = <FILE>; close(FILE); foreach (@data) { print $_; } これだと、ファイルの中身が全て出力されてしまいます。

    • ベストアンサー
    • Perl
  • Perl オープン関数について

    Perl オープン関数について いつもお世話になっております。 オープン関数でわからないことがあるのでご教示お願い致します。 ■test.cgi■ open(FILE, ">sled/$log") or die("error :$!"); eval{ flock(FILE, 2) }; print FILE @DATA; close FILE; ■bbstest.cgi■ open(FILE, ">>sled/$loglog") or die("error :$!"); eval{ flock(FILE, 2) }; print FILE @DATA; close FILE; 上記のtest.cgi側では「>」これが1個で書き込まれるのですが bbstest.cgi側では「>」これ1つだけですと、すべての内容がリセット され、その時入力した内容だけ残ります。 それを「>>」にすると追加はされるのですが、 test.cgi側では 上へ上へと内容は追加されていくのに対し bbstest.cgi側では 下へ下へ追加されていきます。 書込方法は、両方共 unshift(@DATA,"$NO,$tm,$TITLE,$TEXT,$PASS,$IP\n"); このようにしているのですが、 なぜ、同じ処理で、書込方法がかわるのでしょうか? 目的は上へ上へがいいのですが。。。

    • ベストアンサー
    • Perl
  • perl use strict; と ファイルハンドルについて

    perl use strict; を使うと、単純なエラーが防げると認識していますが、下記スクリプトでは役に立たなかったようで、このエラーを探すのに苦労しました。 他にも、どこかのサブルーチンでsortを使ったスクリプト中で、$aを使った時もおかしな挙動をしたことがあります。 use strict;の使い方は正しいと思うのですが、エラーを出してはくれないものでしょうか? また、このようなエラーをしないために安全なプログラムの書き方はありますか? use strict; open(F,"<file.txt"); while(<F>){ &abc($_); } close(F); sub abc(){ my $str = shift @_; open(F,">>file2.txt"); print F $str; close(F); }

    • ベストアンサー
    • Perl
  • Perlのtruncateについて

    + test.txt 0 1 2 3 + main.cgi open(FH, "+<test.txt"); my @Log = <FH>; truncate(FH, 0); seek($FileHandle, 0, 0); print(FH "4"); close(FH); これで、main.cgiを実行すると、test.txtの中身は 4 1 2 3 となってしまいます。truncateがきいてないようなのですが、なぜでしょうか。 truncate(FH, 0) or die "error"; とすると、errorと表示されてしまいます。 なぜこうなるのでしょうか。 致命的エラーがでるわけでもないので、環境が悪いわけではないような気がするのですが。 flockをしたいのですが、他にファイルの中身を空にする方法とかありませんか。

    • ベストアンサー
    • Perl

専門家に質問してみよう