WEB相談室

Webページ作成に関しての何でも掲示板です。

タイトル:open( LOG, "+<file") について

まさお♪ [WriteDate : Fri Apr 13 10:33:12 2001]

同じ質問を他サイトでもさせていただいているのですが、どうしても
なかなか納得できる回答が得られないので、ここでもさせていただきます。
参照:http://script.lovely.to/bbs/infolng.cgi?print+200104/01040009.txt

以下のperlスクリプトです。

1; open(LOG, "+<logfile");
2; $a = <LOG>;
3; print "tell = " . tell(LOG) . "\n";
4; seek(LOG, 0, 1);
5; print "tell = " . tell(LOG) . "\n";
6; print LOG "bbbbbbbbbb\n";
7; close(LOG);

logfileの中身(実行前)

aaaaaaaaaaaaa\n


3行目と5行目の出力結果が同じなのに、4行目のseekがある/なし
でスクリプト終了後のlogfileの内容が違うのです。
私の思惑では2行目でファイルポインタがお尻にきているので
追記になる(open(>>)と同様)とはずでしたが、実際には
seekがないと追記されず、seekをはずすとlogfileの内容が
変わりません。
"+<"で追記するにはseekが必須なのですか?
だとしたらなぜなのでしょう?



綾小路 [MAIL] [URL] [WriteDate : Fri Apr 13 11:12:10 2001]

seekは必須です。

ちょっとσ(^^)も勉強不足で、最近同じ様な事で悩んでましたので。

理由は、これもちょっと勉強不足なので、想像でしかないんですが、オープンされたファイルの内容をレジスタした、ファイルポインタが、3行目の操作で捨てられてしまう為じゃ無いんでしょうか?
perlの言語ベースを解析していないので何とも言い難いのですが^^;

正確な事は、どなたかもっと技術レベルの高い方が答えてくれることに期待します(^^ゞ
80386までのアセンブラやってたレベルじゃ、今のプログラミングは難しい(^^ゞ

まさお♪ [WriteDate : Fri Apr 13 13:10:39 2001]

ご回答ありがとうございます。

> オープンされたファイルの内容をレジスタした、ファイルポインタが、
> 3行目の操作で捨てられてしまう為じゃ無いんでしょうか?

3行目と5行目のprint文は単なるデバッグ文でして、無くても結果は変わらないのです。

seekを入れれば期待通りの動きをするので、当初の悩みは解決されたのですが、やはりすっきりしません。

seekが無い場合、logfileの結果が「bbbbbbbbbb\n」になるならまだ理解できるのですが、内容が変化しないというのがどうしても解せません・・・

最初はperlのバグかと思いましたが、WinでもSolarisでも、Buildナンバーを問わずに再現するので・・・


B-Cus [WriteDate : Fri Apr 13 15:29:36 2001]

OS のバージョンと perl のバージョンは何ですか?

FreeBSD 4.1-RELEASE と Debian (バージョン不明) の
perl-5.003_03 では seek があってもなくても
 aaaaaaaaaaaa\n
 bbbbbbbbbbbb\n
となりました。

再現する環境がないので、大外しの予感がしますが、
読み込みから書き込みに移る際、stdio ライブラリが
バッファをフラッシュしているか否か、かなぁ?

# もしそうなら、open の後に
#  select(LOG); $|=1; select(STDOUT)
# とか。

あるいは一行読んで EOF になった後に内部で rewind
しているかどうか (そもそも rewind なんて必要ない
かもしれないけれど)。最初の logfile の内容を
 aaaaaaaaaa\n
 hoge\n
として実行するとどうなりますか?

あと、tell, seek, print 全ての関数に or die を付けて、
成功しているか失敗しているか見てみるとか。


B-Cus [WriteDate : Fri Apr 13 15:44:12 2001]

追記。

あと、seek(LOG,0,1) の意味はわかってますか?
「SEEK_CUR (=1) から 0バイト移動」ということなので、
これをやっても現在のポジションからは動きません。

で、これがあるかないかで挙動が違うということは、
seek の発行で EOF な状態がクリアされるか否かの
ような気がするんだがなぁ…。

 print LOG "bbbbbbbbbb\n";

 print LOG "bbbbbbbbbb\n" || or die "$!";
として seek をコメントアウトすれば die しません?

あと、if ( eof(LOG) ){ print "EOF.\n"} を適当な場所に
ちりばめてみたらどうなりますか? $a=<LOG> の後は EOF で、
seek した後は EOF じゃなくなってません?


まさお♪ [WriteDate : Fri Apr 13 15:58:44 2001]

とりあえず・・・・

今のところ環境は、(1)Win2000+perl5.6.0、(2)Solaris2.6(spark)+perl5.004_4
しかありません。

------------------------------------------------------------------

logfile の初期状態:
aaaaaaaaaa\n

スクリプト:
open(LOG, "+<logfile.txt");
$a = <LOG>;
print LOG "bbbbbbbbbb\n";
close(LOG);

結果:
(1)
aaaaaaaaaa\n

(2)
aaaaaaaaaa\n
aaaaaaaaaa\n
bbbbbbbbbb\n

------------------------------------------------------------------

logfile の初期状態:
aaaaaaaaaa\n

スクリプト:
open(LOG, "+<logfile.txt");
$a = <LOG>;
seek(LOG, 0, 1);
print LOG "bbbbbbbbbb\n";
close(LOG);

結果:
(1)、(2)
aaaaaaaaaa\n
bbbbbbbbbb\n

------------------------------------------------------------------

logfileの初期状態:
aaaaaaaaaa\n
hoge\n

スクリプト:
open(LOG, "+<logfile.txt");
$a = <LOG>;
print LOG "bbbbbbbbbb\n";
close(LOG);

結果:
(1)
aaaaaaaaaa\n
hoge\n

(2)
aaaaaaaaaa\n
hoge\n
aaaaaaaaaa\n
bbbbbbbbbb\n


Win/Solaris、どちらの挙動も不可解です・・・
dieとか、他の実験も後ほどしてみます。



B-Cus [WriteDate : Fri Apr 13 21:08:20 2001]

んー、ちょっと思い出してきたような、そうでないような。

以下、C のお話。stdio ライブラリは入出力可能ですが、
入力用バッファと出力用バッファは共用になってます。
なので、読み込んで、入力バッファにデータがたまっている
状態で出力すると、

 aaaaaaaaaa\n
 hoge\n (これは、よくわからん)
 aaaaaaaaaa\n (入力用バッファにたまっていたデータ)
 bbbbbbbbbb\n (追加しようとしたデータ)

となるんじゃなかったでしたっけ。

*BSD では、入力/出力切り替え時に自動的にバッファをフラッシュ
します。でも、確か Solaris はそうではないはず。man fopen にも
それっぽいことが書いてあるようなないような。たぶんこれは仕様です。

で、perl は stdio ライブラリを呼んでいるだけなので、
perl でも同じ症状がでると。

バッファフラッシュの手段の一つは、fseek(fp, 0, SEEK_CUR) で、
これを perl で書くと、seek(FH, 0, 1) となります。

と思ったら、ちゃーんと C-FAQ にも書いてありますね。
 http://lagendra.s.kanazawa-u.ac.jp/ogurisu/manuals/C-faq/C-faq-12.html


ふじ [URL] [WriteDate : Sat Apr 14 00:37:58 2001]

蛇足に近いですが。
Solaris8 の 5.005_03 で再現しました。
Kondara (Linux) の 5.005_03 では再現せず。

perldoc -f seek を見ると、

> Due to the rules and rigors of ANSI C, on some systems
> you have to do a seek whenever you switch between
> reading and writing.

てな記述があったので (ラクダ本は今手元にないので未確認)、
B-Cus さんのかかれている通りの理由なんじゃないでしょうか。

綾小路 [MAIL] [URL] [WriteDate : Sat Apr 14 03:48:53 2001]

済みません、ちょっとこのスレッドの質問とは関係ないのですが、この問題から派生しそうな事で疑問に思ったことが有ります。
もし、ファイルオープン中に、エラーによってcloseされなかった場合、深刻なサーバダウン繋がらないのでしょうか?
確か、σ(^^)の拙いPG経験と照らし合わせた場合、このような事が起きるとOS(MSDOS)がぶっ飛んだりした記憶が有るのですが^^;
取りあえず、読み書き両用オープンを使うよりも、オープン→読み込み→変数代入→クローズとして置いて、変数操作した後、オープン→変数の内容を書き込み→クローズとして、処理を分けた方が、安全性という面で良いのでは?と思うのですが、如何な物なんでしょう(汗)

D.D. [WriteDate : Sat Apr 14 07:58:49 2001]

MS-DOS だからなのかも(笑)。

「安全性」という面で考えると
「そういうことに対応できる言語を使う」方が better です。

Ruby ならば

ファイル開く
begin
本来の処理
rescus
エラーメッセージなど、エラーが起こったときの処理
ensure
ファイル閉じる
end

となりますか。


B-Cus [WriteDate : Sat Apr 14 20:34:41 2001]

> MS-DOS だからなのかも(笑)。
だからでしょうね。1プロセスの悪さくらいでサーバが落ちて
いたら、安定運用など望めません。

書き込みモードでも読み書きモードでも、プロセスが落ちた
ときにファイルに与えるダメージは同じでしょう。

# 想像でも構いませんが、読み書き両用モードならではの
# 危険性ってありますか? 僕は思いつきません。


それはそれとして、読み書き両用というのは、えてしてアルゴリズムが
複雑になり、より高度な OS の知識を要求し、バグが発生しやすい
ものだという認識があります (使いこなすのが難しいということ)。

なので、個人的には読み書き両用モードは使いませんが、
使いこなせる人が使いこなす分には問題ないでしょう。


綾小路 [MAIL] [URL] [WriteDate : Sat Apr 14 23:44:49 2001]

D.D様
B-Cus様
本来の質問と関係ない物までレスありがとうございますm(_ _)m

まさお様
質問と関係ない物を書いて、ご迷惑をお掛けしましたm(_ _)m
ついでにハンドブックを開いて見たんですが、tellコマンドでファイルポジションの確認をしてみては如何でしょうか??
(コレも外してます?)

回答(必須): 状態:

お名前(必須):

e-mail:

URL:




[戻る]