こんにちはゲストさん。会員登録(無料)して質問・回答してみよう!

解決済みの質問

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)

投稿日時 - 2017-04-05 11:18:57

QNo.9313610

困ってます

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

>なぜこのような結果になるのでしょうか。

「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#でも同様の事が起きます。

投稿日時 - 2017-04-05 13:47:57

お礼

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

投稿日時 - 2017-04-05 15:45:37

このQ&Aは役に立ちましたか?

0人が「このQ&Aが役に立った」と投票しています

回答(2)

ANo.1

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

投稿日時 - 2017-04-05 11:45:20

お礼

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

投稿日時 - 2017-04-05 13:09:43