第 4 章 データ出力の詳細とデータ入力 61
4.5 書式なし入出力文によるバイナリ形式の利用
real x,y,a(10) integer n,i
namelist /option/ x,y,n,a x = 100.0
y = 100.e10 n = 0
do i = 1, 10 a(i) = i enddo
read(10,option)
のようにプログラムを書いたとします.入力ファイルとしてfort.10という名のファイルに,
&option x=10.0, a(3)=5.0 /
と書き込んでおけば,read文実行後,xは変更されますが,yやnはそのままです.配列aの場合には,
代入された要素a(3)のみが変更されます.
なお,ネームリストに登録された変数の内容は,次のネームリスト出力文で出力することもできます.
write(nd,ネームリスト名)
この場合,全登録変数が“変数=データ値”という形で出力されます.もっとも,ネームリスト出力文を 実行すると,登録された全変数のデータが標準形式で出力されるので,変数が多いと煩雑です.取りあ えず値を確認したいとき以外は,あまり使わない方が良いと思います.
書式なしwrite文で作成したバイナリ形式ファイルから,書式なしread文を使ってデータを読み込 むときにはいくつか注意が必要です.まず,write文で出力したときのデータと同じ数値型の変数を同 じ順番でread文に並べる必要があります.例えば,
real x,y integer n x = 10.0 y = 100.0 n = 10
write(20) x,n,y
というプログラムで作成されたfort.20というファイルから数値を入力するには,
real x,y integer n read(20) x,n,y
のように書かなければなりません.この場合,xもnも同じ10だからと思って,
read(20) n,x,y
と入力すると,出力と数値型の異なるnとxには,正しい値が代入されません.
また,テキスト出力のような“改行”はありませんが,write文1回ごとに印(ヘッダ)が付くので,
write文1回の出力に対し,read文1回で入力しなければなりません26.ただし,write文1回で書き 込まれたデータ数よりもread文1回で入力するデータが少ないのは問題ありません.このとき入力し なかったデータは読み飛ばしたことに相当します.例えば,上記のfort.20のデータを,
read(20) x,n read(20) y
のように2行のread文に分けて入力しようとすると,1行目のread文実行時におけるxとnには正常 な値が代入されますが,2行目のread文実行時に「入力データがない」というエラーで強制終了します.
逆に,上記のwrite文を修正して,
write(20) x,n write(20) y
という2行で出力したファイルから,
read(20) x,n,y
のように,read文1回で読み込むこともできません.
書式なしwrite文は,1回で出力できるデータ数に特に制限はありません.このため,大きな配列を
1回で出力することも可能です.例えば,
real p(10000),q(10000),r(10000) write(30) p,q,r
のように,1回のwrite文で複数の配列の全要素を出力することができます.
もっとも,出力用のプログラムと入力用のプログラムのメンテナンスを考えれば,次のように出力す る配列要素数をあらかじめ出力しておいた方が良いと思います.
26このように,書式なしwrite文を使って保存したバイナリ形式ファイルには,データだけでなくFortran独自のヘッダが 付加されています.このため,C言語など,他のプログラミング言語で作成したプログラムから読み込む場合や,データ解析 ソフトを使って解析するときにはうまく入力できない場合があります.そのような利用が目的でデータを保存するときは,テ キスト形式で保存した方が良いでしょう.
real p(10000),q(10000),r(10000) write(30) 10000
write(30) p,q,r
これを入力するときは,出力数を考慮して real p(10000),q(10000),r(10000) integer i,imlx
read(30) imax
read(30) (p(i),i=1,imax),(q(i),i=1,imax),(r(i),i=1,imax)
のように書きます.こうしておけば,出力用のプログラムを修正して要素数を減らしても,入力用のプ ログラムの変更は不要です.
4.6 ファイルのオープンとクローズ
write文やread文を使ってファイルから入出力をする場合,何も指定がなければ,装置番号ndを付
加した“ fort.nd”という名のファイルを使用します.これに対し,任意の名前を持つファイルを使いた
いときには,open文を使って入出力文の実行前にファイル名を指定しておきます.これを“ファイルを オープンする”といいます.open文は以下の形式です.
open(nd,file=name[, form=format][, status=stat][, err=num])
[ ]は,その内容が省略可能という意味です.それぞれの記述(制御指定子)の意味を表4.3に示します.
表4.3 open文の制御指定子の意味
指定子 指定情報 指定子の意味と注意
nd 装置番号 整数を与える(整数型変数や整数式を与えることも可能) オープンした後,read文やwrite文の装置番号として使う name ファイル名 文字列で指定する(文字変数も可能)
ファイル名は大文字・小文字を正しく指定する必要がある format ファイル形式
文字列で指定する
省略するとテキスト形式の入出力が仮定される
バイナリ形式の入出力を使うときは「’unformatted’」を指定する
stat ファイル情報
文字列で指定する
既存のファイルを使うときは「’old’」を,存在しないファイルを使 うときは「’new’」を指定する
条件に合わないとエラーが発生する
num 文番号 エラーのときにジャンプする行の文番号を指定する
省略すると,エラーが起きたときにはプログラムが強制終了する 例えば,装置番号10のテキスト形式ファイルを“ text.out ”という名にするときは,
open(10,file=’text.out’)
と書きます.また,装置番号30のバイナリ形式ファイルを“ binary.dat ”という名にするときは,
open(30,file=’binary.dat’,form=’unformatted’)
タが上書きされて消えるので注意が必要です.上書きを防ぎたいときには,
open(30,file=’binary.dat’,form=’unformatted’,status=’new’,err=999)
のように,status=’new’とerrを指定します.statusに’new’を指定すると,ファイルが存在しなけ れば新しく作成し,存在すればエラーになります.そこで,エラーの処理をerrで指定した文番号999 の行に用意しておけば上書きを防ぐことができます.
statusの指定を省略してopen文を実行したとき,指定したファイルが存在しなければ,その名前の
ファイルが新たに作成されます.このとき,そのファイルがread文の入力用であれば,データが入って いないのでread文実行時にエラーになりますが,プログラム終了後も作成された空のファイルが残っ てしまいます.そこで,ファイルが入力用のときは,status=’old’を指定して,その存在をチェックし た方が良いでしょう.例えば,バイナリ形式ファイル“ binary.inp ”を入力ファイルとして装置番号20に 指定するには,
open(20,file=’binary.inp’,form=’unformatted’,status=’old’,err=999)
のように書きます.このopen文では,オープンした時点でファイルが存在しなければ,err=999で指 定した文番号999の行へジャンプします.
ファイルへ出力する場合,write文実行時の出力命令のタイミングと実際にディスクに書き込まれる タイミングは必ずしも一致していません.これは入出力ハードウェアを効率よく運用するために,一時 記憶領域への読み書きが介在するためです.このため,確実に書き込みを完了させたいときにはファイ ルをクローズします.クローズは,次のclose文で行います.
close(nd)
ndはクローズするファイルの装置番号です.例えば,装置番号30のファイルをクローズするときは,
close(30)
のように書きます.close文を実行すると,その時点までに装置番号ndに出力した全てのデータがディ スクに書き込まれます.もっとも,プログラムが正常に終了すれば,全てのファイルが自動的にクロー ズされるので,通常はclose文を書かなくても問題ありません.
ファイルをクローズすると,装置番号ndとopen文で指定したファイル名の関係は途切れます.この ため,同じ装置番号に新たに別のファイルを指定してオープンすることも可能です.また,同じ名前の ファイルを再度オープンすることもできます.ただし,再オープンしてもそれ以前の読み書きの継続に はならず,ファイルの先頭に戻って読み書きをします.すなわち,“巻き戻し”をすることになります.
ファイルを巻き戻すと,read文による入力は一からやり直すことになり,write文による出力はファイ ルの先頭から書き直します.このため,巻き戻してからwrite文で出力すると,それ以前に書き込んだ データは消去されます.
なお,巻き戻すのが目的であれば,rewind文を使うことで,クローズせずともファイルを巻き戻すこ とができます.rewind文とは,次のような文です.
rewind(nd)
ndは再度最初から読み書きするファイルの装置番号です.巻き戻しを利用すれば,まずwrite文を 使ってファイルにデータを出力しておき,次にそれを巻き戻して,read文を使ってそのデータを入力し て使うことも可能です.
演 習 問 題 4
(4–1) 連立常微分方程式:2次のRunge-Kutta法
N+ 1個の質量mのおもりが一直線に並んでいて,隣り合ったおもりはバネ定数kのバネでつながれ ているとする.このおもりの位置を左側から,z0,z1,· · ·,zN とすると,i番目のおもりの運動方程式 はその速度をviとして,
dzi
dt =vi
mdvi
dt =−k(zi−zi−1)−k(zi−zi+1) である.このおもりの運動を2次のRunge-Kutta法で解析せよ.
ここで,2次のRunge-Kutta法とは微分方程式 dx
dt =f(t, x)に対し,時刻tnの値xnから出発して時 刻tn+1=tn+ ∆tの値xn+1を
k1 =f(tn, xn)
k2 =f(tn+ ∆t, xn+ ∆t k1) xn+1=xn+∆t
2 (k1+k2) で計算する方法である.∆tは適当に決める.プログラムは
(1) 時間を進めるメインプログラム
(2) Runge-Kutta法の1ステップを計算するサブルーチン (3) 運動方程式の右辺を計算するサブルーチン
の3個から構成するものとする.さらに,ziやviは引数で与え,mやkはグローバル変数としてモジュー ルとuse文を使って共有するようにせよ.なお,初期条件は以下の通りとする.
zi =hi+gsin(kpi) (i= 0· · ·N) vi = 0
ここで,kpは適当な整数pを使って,kp= 2πp
N で与える.また,端にあるおもり(z0とzN)は動か ないとする.(つまり,計算しないで初期条件のままとする)
(4–2) 連立常微分方程式:4次のRunge-Kutta法
問題(4–1)を修正して4次のRunge-Kutta法で解析せよ.ここで,4次のRunge-Kutta法とは微分方 程式 dx
dt =f(t, x)に対し,時刻tnの値xnから出発して時刻tn+1 =tn+ ∆tの値xn+1を k1=f(tn, xn)
k2=f(tn+∆t
2 , xn+∆t 2 k1) k3=f(tn+∆t
2 , xn+∆t 2 k2) k4=f(tn+ ∆t, xn+ ∆t k3)
∆t