「入出力のすべて」

能書き

Fortranで柔軟な入出力を行うのは困難です。が、使いこなせばそれなりのことは出来るようになります。
ここで紹介する知識はFortran限定でしか使えません、4年生の皆さんはあまりこだわらないこと。
入出力オプション

openのオプション

open文には色々とオプションをつけることができます。
 open(UNIT,FILE[,STATUS][,ACTION][,POSITION][,IOSTAT])
・UNIT=装置番号
 非負整数:別解説参照
・FILE=ファイル名
 文字列:相対パス・絶対パスどちらでも可
・STATUS
 'NEW':ファイルが既に存在するとエラー
 'OLD':ファイルが存在しないとエラー
 'REPLACE':ファイルが無ければ作成、あれば削除してから作成
 'UNKNOWN':(デフォルト)
・ACTION
 'READ':読み込み専用
 'WRITE':書き込み専用
 'READWRITE':(デフォルト)
・POSITION
 'APPEND': write:消去せず最後尾に追記
 'REWIND':(デフォルト)read:1行目から読む write:消去して新しく
・IOSTAT
 整数型変数:入出力エラーコードを返す(正常なら『0』を返す)
 指定すると、エラーが起きても続行。指定しない場合エラーで即座に停止
付ける際は、
 引数名=値(もしくは変数)
の形。

・装置番号
ファイル入出力は、open文でファイルのID(装置番号)を設定した後、 そのIDを通して行われる。
ただし以下の番号には最初から装置が割り当てられている。
 UNIT=5:コンソール(キーボード・標準)入力
 UNIT=6:コンソール(画面・標準)出力
 UNIT=0:エラー出力装置(画面)
これらの番号を上書きしても特に問題はないが、2桁の数字(10-)を使うのが慣例。

openいろいろ

・入力ファイルの丁寧なopen
 integer :: errorCode
 open(10,file="parameter.txt",status='OLD',action='READ',position='REWIND',iostat=errorCode)
 if(errorCode/=0) then
   print *, "IOERROR >> there is no 'parameter.txt'"
   stop
 end if
『iostat』とエラー処理は省いても良い。

・出力ファイルのopen:一般
 integer :: errorCode
 open(11,file="result.txt",status='REPLACE',action='WRITE')

・出力ファイルのopen:上書き禁止
 integer :: errorCode
 character(LEN=128) :: resultFile
 read *, resultFile
 open(11,file=resultFile,status='NEW',action='WRITE',iostat=errorCode)
 if(errorCode/=0) then
   print *, "IOERROR >> '"//trim(resultFile)//"' already exists."
   stop
 end if
『iostat』とエラー処理は省いても良い。

・出力ファイルのopen:既存のファイルを残し、そこにデータを書き足す
 open(11,file="result.txt",action='WRITE',position='APPEND')

read, writeのオプション

read, write文にも色々とオプションをつけることができます。
 read(UNIT,FMT[,IOSTAT])
・UNIT=装置番号
 非負整数:上記参照
・FMT=フォーマット
 文字列:上記参照
・IOSTAT
 整数型変数:
 指定すると、エラーが起きても続行。指定しない場合エラーで即座に停止
   0 : 正常
   -1 : EOF(ファイルの終わりに到達)
   正数 : エラー(エラーコードを返す)
 write(UNIT,FMT[,ADVANCE])
・UNIT=装置番号
 非負整数:上記参照
・FMT=フォーマット
 文字列:上記参照
 ・ADVANCE=改行有無
 'yes'/'no': defaultは'yes'
 ADVANCE='no'を指定すると、出力後に改行されない。
 FMTを具体的に指定しないと(*にすると)エラーとなる。
出力のフォーマット

定義:フォーマット

 print '(フォーマット)', {値}
 write(ファイルID,'(フォーマット)') {値}
『フォーマット』のところに「編集記述子」のリストをカンマ区切りで並べることで、出力形式を指定する。
『'(フォーマット)'』の代わりに『*』とすると、自動で設定される。
read文も指定は出来るが、普通は『*』(自動)で良い。
 
編集記述子一覧
・整数
  Iw[.m] : 整数
           [m桁に満たない場合は0で埋める。例: 001]
           *(拡張機能?) w=0とすると、最低幅で左寄せ
          w7     
 '(I7)'  :   1234
 '(I7.6)': 001234
           m6    
          w0  
 '(I0)'  :1234
・実数
  Fw.d  : 小数点    (w >= d+3)
          *(拡張機能?) w=0とすると、最低幅で左寄せ
  Ew.d[ex] : 指数表示(例:-0.##E+10) (w >= d+7)
             仮数部の絶対値が0.1から1.0の間(0.100...〜0.99...)になる。
        指数部はx桁(デフォルトは環境依存)
  ESw.d[ex] : 理学表記指数(w >= d+7)
              仮数部の絶対値が1から10の間(1.00...〜9.99...)になる。
 ENw.d[ex] : 工学表記指数
            指数が3の倍数,仮数部の絶対値が1から1000の間(1.00...〜999.99...)になる。
  Dw.d  :(倍精度実数。指数記号EがDになるだけ。)
  Gw.d  :  絶対値の大きさによって,F型とE型を使い分ける(Fortran2008以降だと、整数や文字列型にも使える)
             w13          
 '(F13.4)'  : -123456.7890
                      d4  
             w13          
 '(E13.4)'  :  -0.1234E+06
                  d4  
             w13          
 '(E13.4e4)':-0.1234E+0006
                d4    x4  
             w13          
 '(ES13.4)' :  -1.2345E+05
                  d4  
             w13          
 '(EN13.4)' :-123.4567E+03
                  d4  
             w13          
 '(G13.4)'  :   0.6022E+24
 '(G13.4)'  :       2.7182
注:環境によって指数部の表示桁数が違うかもしれない。
・その他
  nX    : 空白n個 (n省略不可)
  Aw    : 文字列 (w省略可能)
          左寄せ
  Tn    : タブ揃え
          次の印字をn文字目から開始
  /     : 改行 カンマはいらない
  $     : 改行しない(停留入出力)
  "文字":そのまま出力される
私自身、全て使っているわけではないので少々あいまい。

フォーマットに変数を使う(拡張機能)

フォーマットの中で使いたい変数を< >で囲む。
 <nx>ES<digit+7>.<digit> 
拡張機能なので、ifort以外の環境では使えないかも。
文字列の操作

定義:文字列型変数の操作

文字列の結合
  文字列//文字列
文字列の右側の余白をトリミングする関数
  trim(文字列)
文字列を左詰にする関数
  adjustL(文字列)
trim関数は右側の余白しか削ってくれないので、左の余白も削除したい場合は『trim(adjustL(文字列))』とする。

学習:文字列型

文字列の代入:
文字列を代入する際は、左詰で代入される。よって、短い文字列を代入すると右側に余白ができ、長い文字列を代入すると右側が切り捨てられる。

文字列型変数は、1次元部分配列のように要素指定が出来る。
 str = "1234567890"
 print *, str(1:5)
 str(6:8) = "abc"
 print *, str(2:)
 12345
 2345abc90
1文字だけ指定したい場合でも、上限と下限を指定しなければならない。例えば『str(2:2)』とする。『str(2)』は不可。

次の関数を利用すると、多少高度な処理が可能になる。
検索文字列が最初[最後]に出てくる開始位置を返す関数
  index(文字列変数,検索文字列[,back])
例)ファイル名(拡張子の前)に文字列を追加する
 fileName = "test.txt"
 print *, "input file : ", trim(fileName)
 mainPart  = fileName(1:index(fileName,".",back=.true.)-1)
 extension = fileName(index(fileName,".",back=.true.):)

 fileName   = trim(mainPart) // "_new" // trim(extension)
 print *, "file type : ", trim(extension(2:))
 print *, "new file name : ", trim(fileName)
 input file : test.txt
 file type : txt
 new file name : test_new.txt
Tips (裏技?):変数を文字列にする

定義:文字列型変数への数値の代入

 『変数』の値を『文字列変数』に格納
 write(文字列変数,'(フォーマット)') (数値型)変数
テキストファイルに書き込むのと同じ要領である。

ファイル名の自動生成

write文の装置番号の代わりに変数を入れると、その変数に書き込むことができます。

具体例:
 整数型変数『n』の値を文字列型変数『chn』に格納(左詰)
 write(chn,'(i0)') n
これにより、実行時パラメータをファイル名にすることが可能になります。 すなわち、
 write(chp,'(i3.3)') int(p*100)
 write(chx,'(i0)') nx
 write(chy,'(i0)') ny
 fileName = prefix// &
         & "_p"//trim(chp)// "_x"//trim(chx)// "_y"//trim(chy)// &
         & ".txt"
 open(11,file=fileName,status='new')
とすれば、例えば『prefix="percolation", p=0.5, nx=10, ny=20』のとき
"percolation_p050_x10_y20.txt"
というファイル名が自動的に生成されます。データの管理が格段に楽になるはずです。
上の例にあるように、実数型変数も整数型に変換(int)してから格納すると良い。
「パラメータが多くてファイル名が長すぎる!」という場合は、保存先ディレクトリのパスを自動生成すると良いでしょう。
また、open文の「上書き禁止」(status='new')オプションと組み合わせることで、より安全な運用が可能です。

フォーマットに変数を使う(その2)

write文のフォーマットは文字列なので、この仕組みを使うことでも動的変更が可能です。
前述の方法は簡単だがifort限定機能。この方法は少し面倒だが標準機能の範囲で可能。

たとえば、
 FMT(1:9) = '(es??.??)'
 if(abs(value) < cutoff) then
    write(UNIT,'(i2)',advance='no') 0
 else
    if(value<0) then
       write(FMT(4:5),'(i2)') d+8
    else
       write(FMT(4:5),'(i2)') d+7
    end if
    write(FMT(7:8),'(i2)') d
    write(UNIT,FMT,advance='no') value
 end if
フォーマットの途中にスペースが入っても大丈夫。
とすれば、valueがcutoff以下の時は「 0」、そうでなくても無駄な空白を省くよう出力する。
出力ファイルがディスク容量を圧迫するようなら、1桁でも少なくするよう心がけるべきです。