8
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Fortranで装置番号ではなくファイル名に基づいてファイルを操作する

Last updated at Posted at 2022-06-04

概要

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標準ライブラリに取り込んでもらうなど,より簡便に利用できるような仕組みを考えています.

8
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?