概要
inquire
文を利用して,ファイル名と装置番号を紐付けることで,明示的に装置番号を扱わないようにします.
テスト環境
- Windows 10
- gfortran 10.3.0
- intel fortran 2021.5.0
- nag fortran 7.1
Fortranにおけるファイル操作
Fortranは,その歴史的経緯から,ファイルやその他入出力機器を装置と称し,各装置に番号をふって管理します.
例えば,標準出力には5番,標準入力には6番という装置番号が使われることが多くありました.現在は,output_unit
, input_unit
という既定の定数が設けられています.
ファイルも装置として扱われるので,ファイルを開く場合には装置番号とファイルを紐付ける必要があります.下記の例は,test.txt
という名前ファイルを開き,test
という文字列を書き込んで閉じる処理です.
use, intrinsic :: iso_fortran_env
integer(int32) :: unit_number
character(256) :: iomsg
open (newunit=unit_number, file="test.txt")
write(unit_number, *) "test"
close(unit_number)
装置を開くにはopen
文,装置を閉じるにはclose
文を用います.装置番号は,ファイルを開く際にユーザ側で決める必要があり,結構面倒だったのですが,Fortran 2008からは空いている番号に割り振る機能が追加されました.newunit
指定子がそれに相当します.
ファイルの状態(読み取り専用か,ファイルは既に存在しているか等)を細かく指定することもできます.
use, intrinsic :: iso_fortran_env
integer(int32) :: unit_number, iostat
character(256) :: iomsg
open (newunit=unit_number, file="test.txt", action="readwrite", status="unknown", iostat=iostat, iomsg=iomsg)
write(unit_number, *) "test"
close(unit_number)
装置番号は,C言語におけるFILE
型のポインタ変数に近いと考えると,理解が容易かも知れません.
newunit
でファイルの装置番号を決めなくてもよくなったとはいえ,装置番号を管理するために変数が余分に必要になります.もう少し簡単に,ファイル名だけでファイルを開いたり,書き込んだりしたいと思うのは自然なことだと思います.
inquire
文を用いると,それが実現できます.
inquire
文
問い合わせ文とも呼ばれます.open
文と対になるような文で,装置番号やファイル名から状態(読み取り専用として開かれているか等)を取得します.
test.txt
という名前のファイルに割り振られた装置が開かれているか,その装置番号は何番かを確認するには,下記のプログラムを実行します.
use, intrinsic :: iso_fortran_env
integer(int32) :: unit_number
logical :: is_unit_opened
inquire (file="test.txt", opened=is_unit_opened, number=unit_number)
inquire
の問い合わせ指定子は非常に多く,規格では40個ほどの問い合わせ指定子が定義されています.指定子を一つずつ解説することは,分量の都合でできないので,気になった方は調べてみてください.
inquire
文を利用して,ファイル名から装置番号を返す関数を作る事で,明示的に装置番号を扱わないようにします.
ファイル名から装置番号を返す関数
ファイル名を受け取り,装置番号を返す関数を作成します.動作は非常に単純です.
まず,inquire
文を用いて引数で取得したファイルが開かれているかの状態と,その装置番号を取得します.ファイルが開かれているのであれば,装置番号が正しく取得されているので,return
で呼出し元に戻ります.ファイルが開かれていない場合は,open
文を利用してファイルを開き,装置番号はnewunit
を利用して自動で割り振ります.
!>ファイル名と対応する装置番号を返す.
!>ファイルが存在しない場合は新しく装置を開く.
function get_unit_number(filename) result(unit_number)
implicit none
character(*), intent(in) :: filename
!! ファイル名
integer(int32) :: unit_number
!! 戻り値
!! ファイル名に紐付いた装置番号
logical :: is_unit_opened
! 引数で指定したファイル名のファイルが,装置として開かれているかを問い合わせる
inquire (file=filename, opened=is_unit_opened, number=unit_number)
! 開かれていたらunit_numberが取得されているのでreturnする
if (is_unit_opened) return
! 開かれていなかったらファイルを開き,その装置番号を返す
open (newunit=unit_number, file=filename)
end function get_unit_number
利用する際は,write
文やclose
文の装置番号指定を,関数呼び出しに変更するだけです.ファイルが開かれていなくても,関数の中で開かれるのでopen
文は不要です.
write (get_unit_number("test.txt"), *) "test"
close (get_unit_number("test.txt"))
同じような動作をするopen
関数が,Fortran標準ライブラリのioモジュールに定義されています.
use :: stdlib_io
write (open("test.txt", "w"), *) "test"
ただし,open
関数には,既に開かれているファイルの装置番号を返す機能がないため,装置番号を戻り値として受け取る変数が必要になります.
まとめ
装置番号を明示的に扱わないようにするため,ファイル名から装置番号を返す関数を作成しました.
既に開かれているファイルの装置番号の取得にはinquire
文,新しく装置を開く場合の装置番号の決定には,open
文のnewunit
指定子を利用しました.
「装置番号を変数で取り扱うのが面倒だ」という要求を,関数で解決するのは,関数の管理という新たなめんどくささを導入しただけのような気もしますが,Fortran Package Manager形式のパッケージとして公開する,あるいはFortran標準ライブラリに取り込んでもらうなど,より簡便に利用できるような仕組みを考えています.