- ベストアンサー
Perlのopen()で+<を使用した時の挙動が変
- open()で+<を使用すると読み書き両方ができると理解しましたが、書き込んだ後の結果が私が思っていた挙動と違っていました。
- 例えば、open(FILE,'+<test.txt') or die "$!"; my $line=<FILE>; print FILE "ABC\n"; close(FILE); を実行すると、test.txtファイルの内容が予想とは異なっていました。
- なぜこのような結果になるのでしょうか?
- みんなの回答 (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#でも同様の事が起きます。
その他の回答 (1)
- m-take0220
- ベストアンサー率60% (477/782)
ファイルを読み書き両用でオープンしても、書き込みはファイルの最後から行われます。 ファイルの途中を書き換えることは出来ません。
お礼
書き込みがファイルの最後から行われた場合は ---test.txt---- 123 456 789 ABC ---------------- となりますが123も書き込まれているのはなぜですか。
お礼
なぜこのようなことが起きているのか、 不可思議な現象の的確な解説と、具体的な対処方法、 ファイルポインタを同じ位置に移動させるだけなのに seek FILE,0,1を実行しなければならない理由など、 私が知りたかったことすべてがここに説明されています。 とても分かりやすい説明ありがとうございました。