Sun Studio 12: Fortran プログラミングガイド

第 2 章 Fortran 入出力

この章では、Sun Studio の Fortran 95 コンパイラが提供する入出力機能について説明します。

2.1 Fortran プログラムからのファイルの探査

プログラムとデバイスまたはファイルとの間でデータの転送は、Fortran 論理ユニットを通じて行います。論理ユニットは、入出力文において、論理ユニット番号で識別されます。 論理ユニット番号は、0 から 4 バイト整数の最大値まで (2,147,483,647) です。

文字 * が論理ユニット識別子として現れることがあります。アスタリスクは、READ 文に現れたときは標準入力ファイル、 WRITE 文または PRINT 文に現れたときは標準出力ファイルを表します。

Fortran 論理ユニットは、OPEN 文を通じて、特定の名前付きファイルに関連付けることができます。また、割り当て済みユニットは、プログラムの実行開始時に自動的に特定のファイルに関連付けられます。

2.1.1 名前付きファイルに探査する

OPEN 文の FILE= 指定子は、実行時に、名前付き物理ファイルへの論理ユニットの関連付けを行います。ファイルはあらかじめ存在しているものでもかまいません。また、プログラムの実行時に作成することもできます。

OPEN 文の FILE= 指定子は、簡単なファイル名 (FILE='myfile.out') を指定することも、絶対ディレクトリパスか相対ディレクトリパスを前に付けたファイル名 (FILE='../Amber/Qproj/myfile.out') を指定することもできます。また、指定子は、文字定数、変数、文字式のどれでもかまいません。

ライブラリルーチンを使用して、コマンド行引数と環境変数を文字変数としてプログラムに渡し、OPEN 文でファイル名として使用できます。

次の例 (GetFilNam.f) は、入力された名前から絶対パスファイル名を作成する 1 つの方法を示しています。このプログラムは、ライブラリルーチンの GETENVLNBLNKGETCWD を使用して $HOME 環境変数の値を返し、文字列中の最後の空白以外の文字を見つけ、現在の作業用ディレクトリを決定します。


      CHARACTER F*128, FN*128, FULLNAME*128
      PRINT*, 'ENTER FILE NAME:'
      READ *, F
      FN = FULLNAME( F )
      PRINT *, 'PATH IS: ',FN
      END

      CHARACTER*128 FUNCTION FULLNAME( NAME )
      CHARACTER NAME*(*), PREFIX*128
C           これは、C シェルを仮定しています。
C           絶対パス名は変更しません。
C           '~/' で名前を始めると、チルド (~) はホームディレクトリに
C           置換されます。
C           それ以外の場合、現在のディレクトリのパスを
C           相対パス名の前に置きます。
      IF ( NAME(1:1) .EQ. '/' ) THEN
            FULLNAME = NAME
      ELSE IF ( NAME(1:2) .EQ. '~/' ) THEN
            CALL GETENV( 'HOME', PREFIX )
            FULLNAME = PREFIX(:LNBLNK(PREFIX)) //
     1                     NAME(2:LNBLNK(NAME))
      ELSE
            CALL GETCWD( PREFIX )
            FULLNAME = PREFIX(:LNBLNK(PREFIX)) //
     1                     '/' // NAME(:LNBLNK(NAME))
      ENDIF
      RETURN
      END

GetFilNam.f のコンパイルと実行の結果は、次のようになります。


demo% pwd
/home/users/auser/subdir
demo% f95 -o getfil GetFilNam.f
demo% getfil
 ENTER FILE NAME:
getfil
 PATH IS: /home/users/auser/subdir/atest.f

demo%

これらのルーチンについての詳細は、「2.1.4 ファイル名をプログラムに渡す」を参照してください。また、getarg(3F)、getcwd(3F)、および getenv(3F) のマニュアルページも併せて参照してください。『Fortran ライブラリ・リファレンス』には、そのほかの実用的なライブラリルーチンについても記述されています。

2.1.2 名前を指定しないでファイルを開く

OPEN 文には名前を指定する必要はありません。実行時システムがいくつかの規約に従い、ファイル名を補います。

2.1.2.1 一時ファイルとして開く場合

OPEN 文で STATUS='SCRATCH' を指定すると、システムは tmp.FAAAxnnnnn という形式の名前でファイルを開きます。nnnnn は現在のプロセス ID で置き換えられます。AAA は 3 文字の文字列を示し、x は英字を示します。AAAx によってファイル名が一意になります。プログラムを終了するか、CLOSE 文を実行すると、このファイルはただちに削除されます。FORTRAN 77 互換モード (-f77) でコンパイルするときに、この一時ファイルが削除されないようにするには、CLOSE 文に STATUS='KEEP' を指定します。これは規格外の拡張機能です。

2.1.2.2 すでに開いている場合

すでにプログラムによってファイルが開かれている場合は、そのあとの OPEN 文を使用して、BLANKFORM などのファイルの特性を変更できます。この場合は、変更するファイルの論理ユニット番号とパラメータだけを指定します。

2.1.2.3 あらかじめ接続されているか暗黙の名前付きユニット

プログラムの実行開始時、3 つのユニット番号が自動的に特定の標準入出力ファイルに関連付けられます。あらかじめ接続されるユニットは、標準入力、標準出力、標準エラーです。

通常、標準入力は、ワークステーションのキーボードから入力を受け取ります。標準出力と標準エラーは、ワークステーションの画面に出力を表示します。

そのほかの場合、つまり、OPEN 文に論理ユニット番号を指定し、「FILE= 名前」を指定しない場合、ファイルは fort.n という形式の名前で開かれます。n は論理ユニット番号です。

2.1.3 OPEN 文を使用せずにファイルを開く

デフォルトの規約が想定できる場合 OPEN 文は任意で、使用しなくてもかまいません。論理ユニットへの最初の操作が OPEN または INQUIRE 以外の入出力文である場合は、ファイル fort.n が参照されます。n は論理ユニット番号です。特別な意味を持つ、0、5、6 を除きます。

これらのファイルは、プログラムの実行の前に存在する必要はありません。ファイルへの最初の操作が OPEN 文または INQUIRE 文でない場合、ファイルは作成されます。

例: 次のコード中の WRITE 文がユニット 25 に発行される最初の入出力文である場合、ファイル fort.25 が作成されます。


demo% cat TestUnit.f
      IU=25
      WRITE( IU, '(I4)' ) IU
      END
demo%

このプログラムは、ファイル fort.25 を開いて、そのファイルに書式付きレコードを 1 つ書き込みます。


demo% f95 -o testunit TestUnit.f
demo% testunit
demo% cat fort.25
  25
demo%

2.1.4 ファイル名をプログラムに渡す

ファイルシステムは、Fortran プログラム中の論理ユニット番号を自動的に物理ファイルに関連付けるための機能を持っていません。

しかし、Fortran プログラムにファイル名を渡す方法はいくつかあります。

2.1.4.1 実行時引数と GETARG を経由する

ライブラリルーチン getarg(3F) を使用して、実行時にコマンド行引数を文字変数に読み込むことができます。引数はファイル名として解釈され、OPEN 文の FILE= 指定子で使用されます。


demo% cat testarg.f
         CHARACTER outfile*40
C  ユニット 51 の出力ファイル名として最初の引数を取得する
         CALL getarg(1,outfile)
         OPEN(51,FILE=outfile)
         WRITE(51,*)  'Writing to file: ', outfile
         END
demo% f95 -o tstarg testarg.f
demo% tstarg AnyFileName
demo% cat AnyFileName
 Writing to file: AnyFileName
demo%

2.1.4.2 環境変数と GETENV を経由する

同様に、ライブラリルーチン getenv(3F) を使用して、実行時に環境変数の値を文字変数に読み込むことができます。この値はファイル名として解釈されます。


demo% cat testenv.f
         CHARACTER outfile*40
C  ユニット 51 の出力ファイル名として $OUTFILE を取得する
         CALL getenv('OUTFILE',outfile)
         OPEN(51,FILE=outfile)
         WRITE(51,*) 'Writing to file: ', outfile
         END
demo% f95 -o tstenv testenv.f
demo% setenv OUTFILE EnvFileName
demo% tstenv
demo% cat EnvFileName
 Writing to file: EnvFileName
demo%

getarg または getenv を使用するときには、前後の空白に気をつけるようにしてください。Fortran 95 プログラムは組み込み関数 TRIM を使用でき、古い FORTRAN 77 はライブラリルーチン LNBLNK() を使用できます。この章のはじめにある例の FULLNAME 関数行を用いれば、相対パス名を利用できるようにもプログラムできます。

2.1.4.3 コマンド行における入出力のリダイレクトとパイプ

物理ファイルをプログラムの論理ユニット番号と関連付けるもう 1 つの方法は、あらかじめ接続された標準入出力ファイルをリダイレクトまたはパイプする方法です。リダイレクトやパイプは、実行時の実行コマンド上で行われます。

この方法において、標準入力 (ユニット 5) を読み取り、標準出力 (ユニット 6) か標準エラー (ユニット 0) に書き込むプログラムはリダイレクト、つまりコマンド行上で <, >, >>, >&, |, |&, 2>, 2>&1 を使用することによって、ほかの名前付きファイルを読み取ったり、書き込んだりできます。

これを次の表に示します。

表 2–1 csh/sh/ksh のコマンド行におけるリダイレクトとパイプ

処理 

C シェルを使用する場合 

Bourne または Korn シェルを使用する場合 

標準入力 — mydata から読み取る 

myprog < mydata

myprog < mydata

標準出力 — myoutput に書き込む(上書き) 

myprog > myoutput

myprog > myoutput

標準出力 — myoutput に書き込む(追加) 

myprog >> myoutput

myprog >> myoutput

標準エラーをファイルにリダイレクトする 

myprog >& errorfile

myprog 2> errorfile

標準出力をほかのプログラムの入力としてパイプする 

myprog1 | myprog2

myprog1 | myprog2

標準エラーと標準出力をほかのプログラムにパイプする 

myprog1 |& myprog2

myprog1 2>&1 | myprog2

コマンド行におけるリダイレクトとパイプについての詳細は、cshksh、および sh のマニュアルページを参照してください。

2.2 直接探査入出力

直接探査入出力、つまりランダム入出力を使用すると、レコード番号によってファイルに直接探査できます。レコード番号は、レコードが書き込まれたときに割り当てられます。順番入出力とは異なり、直接探査出力レコードは、どのような順番でも読み取り、あるいは書き込みできます。しかし、直接探査ファイルでは、すべてのレコードが同じ固定長でなければいけません。直接探査ファイルは、そのファイルの OPEN 文の ACCESS='DIRECT' 指定子によって宣言されます。

直接探査ファイル中の論理レコードは、OPEN 文の RECL= 指定子によって指定されたバイト長を持つ文字列です。 READ 文と WRITE 文で、定義されたレコードサイズより大きな論理レコードを指定してはいけません。レコードサイズはバイト数で指定します。論理レコードのほうが短い場合は差し支えありません。書式なし直接探査書き込みでは、レコード中の書き込まれていない部分は不定となります。書式付き直接探査書き込みでは、書き込まれていないレコードは空白で埋められます。

直接探査の READ 文と WRITE 文には、REC=n 引数が追加されており、読み取りや書き込みを行うレコード番号を指定するようになっています。

例: 直接探査、書式なし


      OPEN( 2, FILE='data.db', ACCESS='DIRECT', RECL=200,
&             FORM='UNFORMATTED', ERR=90 )
      READ( 2, REC=13, ERR=30 ) X, Y

このプログラムでは、ファイルを直接探査、書式なし入出力、200 バイトの固定記レコードで開いたあとで、13 番目のレコードを X と Y に読み込みます。

例: 直接探査、書式付き


      OPEN( 2, FILE='inven.db', ACCESS='DIRECT', RECL=200,
&             FORM='FORMATTED', ERR=90 )
      READ( 2, FMT='(I10,F10.3)', REC=13, ERR=30 ) X, Y

このプログラムでは、ファイルを直接探査、書式なし入出力、200 バイトの固定レコード長で開きます。次に 13 番目のレコードを読み取り、(I10,F10.3) の書式を使用して変換します。

書式付きファイルの場合、書き込まれるレコードのサイズは、FORMAT 文によって決定されます。前述の例では、FORMAT 文はレコードを 20 文字、または 20 バイトに定義しています。並び上のデータの量が FORMAT 文で指定されたレコード長より大きい場合、1 つの書式付き書き込みで複数のレコードを書き込むことができます。このような場合、各後続のレコードには、連続するレコード番号が割り当てられます。

例: 直接探査、書式付き、複数レコード書き込み:


      OPEN( 21, ACCESS='DIRECT', RECL=200, FORM='FORMATTED')
      WRITE(21,'(10F10.3)',REC=11) (X(J),J=1,100)

直接探査装置 21 への書き込みによって、10 の要素からなる 10 のレコードが作成されます。 なぜなら、レコードごとに 10 要素であると書式で指定しているからです。 これらのレコードには、11 から 20 までの番号が割り当てられます。

2.3 バイナリ入出力

Sun Studio Fortran 95 では OPEN 文の機能が拡張されて「バイナリ」の入出力ファイルを宣言できるようになりました。

ファイルを開くときに FORM='BINARY' と指定すると、レコード長がファイルに組み込まれないことを除いて、FORM='UNFORMATTED' とだいたい同じ結果になります。このデータがなければ、1 レコードの開始点と終了点を知らせる方法がありません。そのため、後退する場所を知らせることができないので、FORM='BINARY' ファイルに対して BACKSPACE を実行できません。'BINARY' ファイルに対して READ を実行すると、入力リストの変数を設定するために必要な量のデータが読み込まれます。

2.4 ストリーム入出力

f95 には、Fortran 2003 規格の新しいストリーム入出力スキームが実装されています。ストリーム入出力アクセスは、データファイルを、1 から始まる正の整数でアドレス指定できる連続バイト列として扱います。OPEN 文で、ストリーム入出力を、ACCESS='STREAM' 指定子をつけて宣言してください。バイトアドレスへファイルを位置付けると、READ または WRITE 文に POS=scalar_integer_expression 指定子が必要になります。 INQUIRE 文では、ACCESS='STREAM'、指定子 STREAM=scalar_character_variable、および POS=scalar_integer_variable が使用できます。

ストリーム入出力は、C プログラムで作成するファイルまたは読み取るファイルと相互に運用する場合に便利です。次にその例を示します。


C の fwrite() で作成したファイルを Fortran 95 プログラムで読み取る

program reader
 integer:: a(1024), i, result
 open(file="test", unit=8, access="stream",form="unformatted")
! a のすべてを読み取る
 read(8) a
 do i = 1,1024
   if (a(i) .ne. i-1) print *,'error at ', i
 enddo
! ファイルを逆方向に読み取る
 do i = 1024,1,-1
   read(8, pos=(i-1)*4+1) result
   if (result .ne. i-1) print *,'error at ', i
 enddo
 close(8)
end

C プログラムでファイルに書き込む

#include <stdio.h>
int binary_data[1024];

/* 32 ビットの整数を 1024 個含むファイルを作成する */
int
main(void)
{
    int i;
    FILE *fp;

    for (i = 0; i < 1024; ++i)
        binary_data[i] = i;
    fp = fopen("test", "w");
    fwrite(binary_data, sizeof(binary_data), 1, fp);
    fclose(fp);
}

C プログラムでは、fwrite() を使用して、ファイルに 1024 個の 32 ビット整数を書き込んでいます。Fortran 95 のプログラム読み取りは、これらの整数をまず配列として読み込んでから、ファイルの最後から先頭まで逆方向に個々の整数を読み込んでいます。2 番目の read 文に含まれる pos= 指定子を見ると、位置がバイト 1 から始まるバイト数で指定されていることがわかります (C では、バイト 0 から始まります)。

2.5 内部ファイル

内部ファイルは、変数、部分列、配列、配列要素、構造化レコードの欄のような、CHARACTER 型のオブジェクトです。内部ファイルからの READ の場合は、文字列の定数であってもかまいません。内部ファイルにおける入出力は、データをある文字実体からほかのデータ実体に転送し、変換することによって、書式付き READ WRITE 文をシミュレートします。ファイルの入出力は実行されません。

内部ファイルを使用するときには

例: 内部ファイルから書式付きで順番に読み取ります (1 レコードのみ)。


demo% cat intern1.f
      CHARACTER X*80
      READ( *, '(A)' ) X
      READ( X, '(I3,I4)' ) N1, N2 ! 内部ファイル X を読み取る 
      WRITE( *, * )  N1, N2
      END
demo% f95 -o tstintern intern1.f
demo% tstintern
 12 99
 12 99
demo%

例: 内部ファイルから書式付きで順番に読み取ります (3 レコード)。


demo% cat intern2.f
      CHARACTER  LINE(4)*16
      DATA  LINE(1) / ' 81  81 ' /
      DATA  LINE(2) / ' 82  82 ' /
      DATA  LINE(3) / ' 83  83 ' /
      DATA  LINE(4) / ' 84  84 ' /
      READ( LINE,'(2I4)') I,J,K,L,M,N
      PRINT *, I, J, K, L, M, N
      END
demo% f95 intern2.f
demo% a.out
   81  81  82  82  83  83
demo%

例: 内部ファイルから -f77 互換モードで直接探査により読み取ります (1 レコード)。


demo% cat intern3.f
      CHARACTER LINE(4)*16
      DATA  LINE(1) / ' 81  81 ' /
      DATA  LINE(2) / ' 82  82 ' /
      DATA  LINE(3) / ' 83  83 ' /
      DATA  LINE(4) / ' 84  84 ' /
      READ ( LINE, FMT=20, REC=3 ) M, N
20       FORMAT( I4, I4 )
      PRINT *, M, N
      END
demo% f95 -f77 intern3.f
demo% a.out
   83  83
demo%

2.6 ビッグエンディアン式プラットフォームとリトルエンディアン式プラットフォーム間のバイナリ入出力

新しいコンパイラフラグの -xfilebyteorder は、SPARC システムと x86 システムとの間でバイナリ入出力ファイルの移動をサポートします。このフラグは、書式なし入出力ファイルのバイト順序とバイト列を特定します。

次に例を示します。

-xfilebyteorder=little4:%all,big16:20

前述のオプションは、"SCRATCH" で開くファイルを除き、Fortran ユニット 20 を除くすべてのファイルに、32 ビット x86 システム等に該当する、4 バイト境界で整列された「リトルエンディアン」データを持つよう指示します。Fortran ユニット 20 は、64 ビット SPARC システム等に該当する、64 ビットの「ビッグエンディアン」ファイルになります。

詳細は、f95(1) のマニュアルページ、および『Fortran ユーザーズガイド』を参照してください。

2.7 そのほかの入出力について

Fortran 95 のプログラムと古い FORTRAN 77 のプログラムには、入出力の互換性があります。f77 のコンパイルと f95 のコンパイルが混在する実行ファイルでは、プログラムの f77 部分と f95 部分のどちらからでも同じ装置に対して入出力を行えます。

ただし、Fortran 95 には次のような新しい機能が追加されています。


       write(*,'(a)',ADVANCE='NO')  'Enter size= '
       read(*,*) n

Fortran 95 の入出力拡張機能、および f95f77 の違いについての詳細は、『Fortran ユーザーズガイド』を参照してください。