はじめに
f90ファイルでopen文を書く際に,これまではあまり深く考えずにstatus='unknown'
を使っていたが,罠にハマったので記録しておく.結論としてはunknown
はできるだけ使わずに,statusは明示(new
, old
, replace
)するべき.
2020/06/23 コメントを受けての追記:以下の「罠」はunknown
+stream
の組み合わせとしては仕様通りの振る舞いを示していると思われる.ただし,結論は変わらない.
罠
次のようなバイナリ形式のファイル出力を行う.ちなみにform='unformatted',access='stream'
とform='binary'
は同義であるが,後者はgfortranでコンパイルできない(Fortran でのバイナリ (unformatted, binary) の扱いについてのメモ).
program unknown_test
use,intrinsic :: iso_fortran_env
implicit none
integer(int32) unit_no
open(newunit=unit_no,file='my_name_is_unknown.dat',form='unformatted',access='stream',status='unknown')
write(unit_no) 'my name is unknown.'
write(unit_no) 1,2,3,4,5,6 !<-あとで増やす(本文参照)
close(unit_no)
end program unknown_test
実行環境はWindows 10でWSL(Ubuntu)でGNUコンパイル
$ gfortran -v
...
gcc version 7.4.0 (Ubuntu 7.4.0-1ubuntu1~18.04.1)
$ gfortran test.f90 && ./a.out && ls -l my_name_is_unknown.dat
-rw-rw-rw- 1 hoge hoge 43 Jun 21 12:42 my_name_is_unknown.dat
平和.43バイトのmy_name_is_unknown.datファイルが出力される.続いて,test.f90で!<-あとで増やす
としてマークしていた行を複製して数値データを増やしたmy_name_is_unknown.datを出力する.
$ gfortran test.f90 && ./a.out && ls -l my_name_is_unknown.dat
-rw-rw-rw- 1 hoge hoge 67 Jun 21 12:46 my_name_is_unknown.dat
まだ平和.ファイルサイズが67バイトに増加している.当然である.続いて,複製した行を削除して元の43バイトファイルが出力されるようにしてみる.
$ gfortran test.f90 && ./a.out && ls -l my_name_is_unknown.dat
-rw-rw-rw- 1 hoge hoge 67 Jun 21 12:47 my_name_is_unknown.dat
...ん?67バイト?......戦争である.
どうしてこうなった
2020/06/23 コメントを受けての追記:ファイルがすでに存在する時点でunknown
で同名ファイルを出力すると,レコード長を指定しない限り(pos=***
なし)は頭から上書きされていき,stream
はEOFを書き出さないため,前回の残り滓がファイル端に残ることになる.
以下,引用→NAG Fortran コンパイラ 5.2 マニュアル
書式なしの順アクセスファイルとは異なり、書式なしストリームファイルに対し ファイル終端より前の位置に書き込む場合にはファイルの切詰めは行われない 点に注意して下さい。(しかし書式付きストリームの場合にはこの切詰めが行わ れます。)
2020/06/23 追記ここまで.
正直分からない.いや,処理系依存のunknown
が悪さをしていることは色々と試すと分かったのだが,どう悪いことをしているのかまでは分からない.
「hexdump for VSCode」(バイナリデータを読むのに便利な VSCode の拡張機能「hexdump for VSCode」の紹介)を使って中身を見てみたが,どうやらバイナリデータが増える方向にはunknown
処理が上書きとして働き,減る方向には上書き処理がキャンセルされているっぽくて,中身が変わっていない.でもファイル時間は更新されているしな...分からん.
ちなみに
- 上記テストでは中身が変わっていなかったが,同様にしてParaView(可視化用オープンソースプログラム)用にVTKファイルを作成したところ,最終形態(なぜかファイルサイズが変わらない版)をParaViewで開くとフリーズする
-
form='formatted'
つまりASCII形式で出力すると問題なく上書きされる
対処方法
冒頭に書いたように,処理系依存のunknown
ではなく,statusは明示(new
, old
, replace
)するべき.たとえば上記テストの場合はreplace
.