8日目「テキストファイル」:準備
能書き
これまで、コンピュータとのデータのやりとりは黒い画面を通して行ってきました。しかし実際に研究で使うとなるとパラメータを毎回キーボードから入力するのは非効率的ですし、 膨大な量のデータは黒い画面の中では収まりません。
ここでは、テキストファイルを使ってデータやりとりする方法を学びます。
ファイルを作る
program textfile !------------------------ ! Fortran template !------------------------ use bacs implicit none !--- begin header --- !--- end header --- !--- begin main --- !--- end main --- end program
Lesson 22. 外部ファイルの読み書き
定義:入出力文
最初に1回外部ファイル開き、IDを振る open (ファイルID,file="ファイル名")ファイルが存在しない場合は、そのファイル名で新規作成される。
ファイルIDは10以上の番号を使いましょう。
途中、必要な回数
『ファイルID』のファイルから値を読み込み、変数に代入 read (ファイルID,*) 変数名[,変数名2,…]
ファイルに値を書き込む write(ファイルID,*) 値[,値2,…]最後に1回
開いた外部ファイルを閉じる close(ファイルID)
実際にはcloseしなくても困らないことが多いが、使い終わったら閉じるのがマナー。
実践
実際にやってみるとわかりやすいでしょう。"input.txt"という名前のファイルを作り(本体と同じ場所)、次のように書き込んで下さい。
# random number seed: iSeed 2311 # system size: Lx, Ly 2 2
1、3行目は人間用の説明文です。
これを読み込むプログラムがこちら。
implicit none integer :: iSeed,Lx,Ly open(10,file="input.txt") !"input.txt"を開く read (10,*) !1行飛ばす read (10,*) iSeed !2行目の値をiSeedに代入 read (10,*) read (10,*) Lx, Ly !2つ連続で読み込む close(10) !-- calculation -- !{メインの計算部分} open(11,file="result.txt") !"result.txt"を開く(新しく作る) write(11,*) "# input parameters #" write(11,*) " iSeed = ", iSeed write(11,*) " Lx = ", Lx, ", Ly = ", Ly close(11) print *, " output > result.txt" !細かい配慮これを実行すると、"input.txt"を読んで"result.txt"に書き込んでくれます。
先に『open』が必要なこと以外は、画面に出力する時と違いはありません。
もう少し実用的な例を次に示します。
Lesson 23. 文字列型
定義:文字列型
"文字列定数" もしくは '文字列定数'
character(len=文字数) :: 変数名header部分に書く(宣言する)ことで、『変数名』を『文字数』字の大きさの文字列型の変数として使えるようになる。
文字列の結合 文字列//文字列
文字列の右側の余白をトリミングする関数 trim(文字列)
文字列関数は他にもありますが、使うことはないでしょう。
学習
卒研における文字列型変数の使い道はただ一つ。出力先ファイル名を指定するために使います。出力データのファイル名に、使用したパラメータを含めることによって、データファイルを管理しやすくなります。
Fortranは文字列を扱うのが基本的に苦手なので、それ以外には使わない方が無難です。
"input.txt"
# random number seed: iSeed 2311 # system size: Lx, Ly 20 30 # output file "wavefunction_Lx20Ly30_2311.txt"本体
implicit none integer :: iSeed, Lx,Ly real(kind=double), allocatable :: Amat(:,:) character(LEN=99) :: outputFile !-- read parameters -- open(10,file="input.txt") read (10,*) read (10,*) iSeed read (10,*) read (10,*) Lx, Ly read (10,*) read (10,*) outputFile close(10) !-- calculation -- !{メインの計算部分} !-- write result -- open(11,file=outputFile) write(11,*) "# input parameters #" write(11,*) " iSeed = ", iSeed write(11,*) " Lx = ", Lx, ", Ly = ", Ly close(11) print *, " output > ", trim(outputFile)これで何の問題もないのですが、だんだんと「空きすぎたスペースを詰めたい」などと欲が出てきます。 そういう場合は次の項目へ。
Lesson 24. 出力形式を指定する
定義:フォーマット
print '(フォーマット)', {値} write(ファイルID,'(フォーマット)') {値}『フォーマット』のところに「編集記述子」のリストをカンマ区切りで並べることで、出力形式を指定する。
『'(フォーマット)'』の代わりに『*』とすると、自動で設定される。
read文も指定は出来るが、普通は『*』(自動)で良い。
編集記述子一覧(もっと詳しく)
Iw : 整数 Fw.d : 小数点表示 (w >= d+3) ESw.d : 指数表示 (w >= d+7) nX : 空白n個 (n省略不可) Aw : 文字列 (w省略可能) "文字":そのまま出力されるw,dには具体的な数字が入り、「w文字幅に、右詰で、小数点以下d桁で書く」という意味になる。
編集記述子の前にn(具体的な数字)をつけると、n個くりかえす。
丸かっこでくくった中身を繰り返すこともできる。
編集記述子のリストと値のリストは一対一対応する必要がある。
正しく指定しないと、エラーになったり"###"(桁あふれ)になったりする。
学習
使いこなすのはなかなか難しいので、最初のうちは試行錯誤でやってみよう。print '(1X,a,": *",f9.2,"*")', "F ", -123.4 print '(1X,a,": *",es9.2,"*")', "ES", -123.4 print '(1X,a,": *",f5.2,"*")', "F ", -123.4
F : * -123.40* ES: *-1.23E+02* F : *#####*
実践
次のような形のプログラミングができれば、卒研は安泰でしょう。"input.txt"
# random number seed 2311 # system size: Lx, Ly 3 3 # number of samples: nsmpl 100 # output file "result_3x3.txt"本体
use bacs use ranpack implicit none integer :: i,j, iSeed, iSmpl,nSmpl, Lx,Ly real(kind=double), allocatable :: Amat(:,:) character(LEN=99) :: outputFile !-- read parameters -- open(10,file="input.txt") !入力ファイル"input.txt"を開く read (10,*) !(変数の説明) read (10,*) iSeed !乱数の種 read (10,*) read (10,*) Lx, Ly !システムサイズ read (10,*) read (10,*) nSmpl !サンプル数 read (10,*) read (10,*) outputFile !出力先ファイル名 close(10) !使い終わったら閉じておく !-- write header on result file -- open(11,file=outputFile) ! 出力先を開いておく write(11,'(a)') "### This is YYY of XXX. ###" !後で見て分かるような説明 write(11,'(a)') "# input parameters #" write(11,'(a,i8)') "# iSeed = ", iSeed write(11,'(2(a,i4))') "# Lx = ", Lx, ", Ly = ", Ly write(11,'(a)') "#" write(11,'(a1,2a3,a12)') "#","x","y","A(y,x) " !ラベル。 データと位置をそろえた !-- initialize -- call ran__dini(iSeed) allocate(Amat(Ly,Lx)) Amat = zero !>>>>> sample loop >>>>> do iSmpl=1,nSmpl !-- calculation -- !何か計算する do j=1,Lx do i=1,Ly Amat(i,j) = ran__drnd() end do end do !-- write result -- !計算する度、欲しい結果を書き出す write(11,*) "# sample = ", iSmpl do j=1,Lx do i=1,Ly write(11,'(1X,2i3,es12.4)') j, i, Amat(i,j) end do end do end do !<<<<< end sample <<<<< close(11) !全てが終わったらclose print *, " output > ", trim(outputFile)
おまけ:行列の表示を画面内におさめる
例えば0か1で構成される整数型の行列A(10x10くらい)があったとしましょう。これを画面上で確認したい時、print *, "matrix A =" do iy=1,ny print *, A(iy,1:nx) end doなどとすると、表示があふれて何のことやらわからなくなってしまいます。
そこで、表示幅を狭く設定すると
print *, "matrix A =" do iy=1,ny print '(99i2)', A(iy,1:nx) end do
ここの『99』は、『nx』より大きい数であれば良い。
ある程度までなら画面の幅に収まるようになります。桁数を減らすと見やすくなるだけでなく、出力ファイルの容量もスリムになります。有効に使いましょう。
ただし有効桁数に注意。