IT pass HikiWiki - [itbase2023]Fortran 実習 入出力 Diff

  • Added parts are displayed like this.
  • Deleted parts are displayed like this.

{{toc}}

データ解析や数値計算には入力データが必要なことが多いでしょう.
また, 解析結果や計算結果は何かしらの形で出力しなければなりません.
ここでは, Fortran での入出力を簡単に体験してみましょう.

= 注意

* 前回説明したように, Fortran のプログラムの実行のためには, エディタでプログラムを書くだけでなく, 「コンパイル」する必要があります.
* 今回の資料では, プログラムのコンパイル方法の詳細は説明していません.
* 意味が解らなければ前回の資料を復習しましょう.


= 入出力文

== 入力文

Fortran では下に示す入力の命令が用意されています.

# RT
  delimiter = %

命令% 入力元 % 使用例
read % キーボードとファイル % read( 5, * ) num

ここで read の引数の "5" と "*" はそれぞれ「装置番号」と「書式」を
表しています. これらについては後で説明します.

なお, read 文には, 装置番号と書式の他にも様々な指定が可能です.
興味があれば調べてみると良いでしょう.

== 出力文

Fortran では下に示す出力の命令が用意されています.

# RT
  delimiter = %

命令% 出力先 % 使用例
print % 画面 % print *, 5.0
write % 画面とファイル % write( 6, * ) 5.0

print は画面にしか出力できませんが, write はそれに加えて
ファイルに出力することもできますので, write の方が用途が
広いことになります.

ここで print および write の引数の "6" と "*" はそれぞれ
「装置番号」と「書式」を表しています.
これらについては後で説明します.


== 装置番号

read と write で入出力する際に, 入力元や出力先を
指定するために使うのが装置番号です.

上の read/write の使用例では, 5 と 6 がそれらに対応します.
装置番号の 5 と 6 は特別な番号で,
5 はキーボード (「標準入力」とも呼びます) を表し,
6 は画面 (「標準出力」とも呼びます) を表します.

従って,

  read( 5, * ) num



  write( 6, * ) 5.0

はそれぞれ, キーボード (標準入力) からの読み込みと画面 (標準出力) への
出力を表しています.

5, 6 以外の数字は, ユーザが決めることができて, ファイルに割り当てて
入出力に使います.


== キーボードから入力と画面への出力の例

下のようなプログラムを inputfromkb.f90 というファイル名で作成して
実行してみましょう.

  program inputfromkb

    implicit none

    real :: val
    real :: val2

    write( 6, * ) "Input a number:"        ! 入力を促す
    read ( 5, * ) val                      ! キーボード (装置番号 5) から読み込み
    write( 6, * ) "Input number:", val     ! 読み込んだ値の表示

    write( 6, * ) "Input two numbers:"     ! 入力を促す
    read ( 5, * ) val, val2                ! キーボード (装置番号 5) から読み込み
    write( 6, * ) "Input numbers:", val, val2  ! 読み込んだ値の表示

  end program inputfromkb

この例では, 一つ目の write 文で数値の入力を促し,
read で値を読み込み,
二つ目の write 文で入力した数値を表示しています.

このプログラムをコンパイルして作った実行ファイル inputfromkb を
実行すると下のようになります.
(プログラムは "Input a number:" と表示して数値の入力を促しますので, キーボードから数値を入力する必要があります.)

  $ ./inputfromkb
   Input a number:
  123.4567                          <- キーボードから数値を入力する
   Input number:   123.456703    <- プログラムが数値を出力
   Input two numbers:
  123.4567   890.123                <- キーボードから数値を入力する
   Input numbers:  123.456703   890.123    <- プログラムが数値を出力


= 書式

上に書いた read, print, write の使用例に現れる "*" は「書式」の指定
を表します.
ここで言う書式とは, 例えば数値を出力する際に, 何桁で出力するのかを
意味します.

上の例で示している "*" の指定は最も簡単で, 「適当に」入出力する指定です.

例えば, 下のプログラムを outputnum.f90 というファイル名で作成し,
コンパイルして実行してみましょう.

  program outputnum

    implicit none

    integer :: num
    real    :: val

    num = 1
    val = 1.234567890

    write( 6, *                                 ) num, ",", val, ",", val
    write( 6, "(i5, x, a1, f5.2, x, a1, e10.2)" ) num, ",", val, ",", val

  end program outputnum

例えばこのプログラムをコンパイルして作った実行ファイル outputnum を
実行すると下のようになります.

  $ ./outputnum
             1 ,   1.23456788     ,   1.23456788
      1 , 1.23 ,  0.12E+01

書式に "*" を指定したことで, 1 行目は「適当に」出力されていますが,
2 行目は "(i5, x, a1, f5.2, x, a1, e10.2)" に従って出力になっています.
この指定のそれぞれは下のような意味があります.

"(i5, a1, f5.2, a1, e10.2)" の指定は,
i5 が num に対する書式指定,
x が空白 1 文字を書く指定,
a1 が一つ目の "," に対する書式指定,
f5.2 が一つ目の val に対する書式指定,
a1 が二つ目の "," に対する書式指定,
x が空白 1 文字を書く指定,
e10.2 が一つ目の val に対する書式指定に対応します.
それぞれ下のような意味になります.

# RT
  delimiter = %

書式指定 % 意味 % 備考
i5 % 整数全 5 桁 %
i5.5 % 整数全 5 桁 % 値が 5 桁に満たない場合は 5 桁になるようにゼロで埋められる
f5.2 % 実数全 5 桁, 小数点以下 2 桁 % 全 5 桁には小数点と空白を含む
e10.2 % 実数全 10 桁, 小数点以下 2 桁 (mmE+-nn の形式) % 全 10 桁には小数点と空白と指数の E, + も含む
a1 % 文字列 1 文字 %
x % 空白 1 文字 % 空白文字 " " を書いて a1 と書式指定しても同じ.

その他のものも含めると, 指定に使う文字 (変換記号) には下のような
ものがあります.

# RT
  delimiter = %

書式指定 % 意味 % 使用例 % 123 を出力した時の結果
i % 整数型 % i5 % "  123"  
   %        % i5.5 % "00123"  
f % 実数型 % f7.2 % " 123.00"
e % 実数型, mmE+-nn の形式 % e9.2 % " 0.12E+03"
d % 倍精度実数型, mmE+-nn の形式 % d9.2 % "0.12D+03"
l % 論理型 % - % -
a % 文字型 % - % -
x % 空白 % - % -

注意: 上の表における, 「123 を出力した時の結果」にある
ダブルクォーテーションは出力されません. 数値の前に空白が入ることを
表すために書いています.

また, 同じ指定を繰り返す時には, まとめて指定することもできます.
例えば, 下のような出力,

    write( 6, "(i5, x, a1, i5, x, a1, e10.2)" ) num, ",", num, "," val

では, i5, x, a1 が 2 回繰り返されています. これらはまとめて,

    write( 6, "(2(i5, x, a1), e10.2)" ) num, ",", num, ",", val

とすることもできます.


== format 文

書式指定が長くなる場合, あるいは, 同じ書式を何度も使う場合には,
下のように書式を別の行に書くこともできます.

  program outputnumformat

    implicit none

    integer :: num
    real    :: val
    real    :: val2

    num = 1
    val = 1.234567890
    val2 = 0.987654321

    write( 6, *   ) num, ",", val, ",", val
    write( 6, 101 ) num, ",", val, ",", val
    write( 6, 101 ) num, ",", val2, ",", val2

  101 format( i5, a1, f5.2, a1, e10.2 )

  end program outputnumformat

ここで使われているのは format 文で, 書式指定のための命令文です.
そして, 101 は, その format 文を指定するために使われているラベルです.
複数の write 文で同じ format 文を使うこともできます.

このような書式指定をすることで, 出力の見た目を整え, 結果を
わかりやすく示すことができます.


#== 練習問題
#
#上に示したプログラム outputnum.f90 を変更して,
#プログラムの実行結果の出力が下のように 1 行目と 2 行目が
#揃うように書式指定と出力変数を工夫してみましょう.
#(やり方は複数あり得ます. 思いついた方法でやってみましょう.)
#
#出力結果の例
#
#  $ ./outputnum
#             1 ,   1.23456788     ,   1.23456788
#             1 ,   1.23           ,   0.12E+01
#
##             1 ,   1.23456788     ,   1.2345678901234567
##             1 ,   1.23           ,   0.12E+01



= ファイルからの入力/への出力

== ファイルを開く

Fortran でファイルを開くためには例えば下のような open 文を用います.

  open( 11, file = "test.txt", status = "unknown" )

一般的には,

  open( <装置番号>, file = <ファイル名>, status = <ステータス>, ... )

となります.

<装置番号> は, read 文や write 文で説明したものと同じものです.
つまり, open は, ファイル名と装置番号を関連付けており,
ファイルからの入力やファイルへの出力には, その入力元/出力先の
ファイルに対応した装置番号を指定します.

ステータスには下に示すような種類を指定することができます.

# RT
  delimiter = %

ステータス % 意味
unknown % 「適当に」開く (読み込みでも書き込みでも)
old % 既に存在するファイルを開く (ファイルが存在しないとエラーになる)
new % 新規作成用に開く (既にファイルが存在するとエラーになる)
replace % 新規作成用に開く (既にファイルが存在すると内容は上書きされる)

open 文には, 装置番号, ファイル名, ステータス以外にも, 様々な
指定が可能です.
それらを使うことでかなり柔軟な処理が可能になります.
ここでは詳細を説明しませんが, 興味があれば調べてみると良いでしょう.


== ファイルを閉じる

Fortran でファイルを閉じるためには下のような close 文を用います.

  close( 11 )

一般的には,

  close( <装置番号> )

となります.

<装置番号> は, read/write/open 文で説明したものと同じものです.
open 文で開いたファイルを閉じるときに, 対応する装置番号を指定します.


== ファイルへの出力の例

下のようなプログラムを outputtofile.f90 というファイル名で作成して
実行してみましょう.

  program outputtofile

    implicit none

    real :: val

    write( 6, * ) "Input a number:"               ! 入力を促す
    read ( 5, * ) val                             ! キーボードから入力

    open( 11, file="output.txt", status="unknown" ) ! ファイルを開く
    write( 11, * ) val                              ! ファイルへの出力
    close( 11 )                                     ! ファイルを閉じる

  end program outputtofile

このプログラムでは, キーボードから入力した数値をファイル, output.txt, に
出力しています.
実行した後で, ファイルができているかどうか, また, ファイルの中に
数値が保存されているかどうかを確認しましょう.


== ファイルからの入力の例

ファイルからの入力を試すために, 入力データを用意しましょう.

emacs を使って, input.txt という名前のファイルに下の内容を書き込みましょう.

  5   105
  10  110
  15  115
  20  110
  25  105

また, 下のようなプログラムを inputfromfile.f90 というファイル名で
作成し, 実行しましょう.

  program inputfromfile

    implicit none

    integer :: val, val2

    open( 11, file="input.txt", status="old" )  ! ファイルを開く
    read ( 11, * ) val                          ! ファイルから数値を一つ入力
    write(  6, * ) val                          ! 画面に数値を出力
    read ( 11, * ) val
    write(  6, * ) val
    read ( 11, * ) val, val2
    write(  6, * ) val, val2
    read ( 11, * ) val, val2
    write(  6, * ) val, val2
    read ( 11, * ) val, val2
    write(  6, * ) val, val2
    close( 11 )

  end program inputfromfile

実行すると下のようになるでしょう.

$ ./inputfromfile
            5
           10
           15         115
           20         110
           25         105

このプログラムは, input.txt から数値を入力し, それを画面に出力しています.
read 文一つにつき, 一つの数値を読んでいることに注意しましょう.
read 文では基本的には 1 行ずつ読み込みます.

== 練習問題

上で使った inputfromfile.f90 を基にして, input.txt に保存されている
数字を読み込み, その和を求めて, その値をファイル output.txt に
出力するプログラムを作りなさい.

=== 答え

* ((<プログラム例|URL:http://itpass.scitec.kobe-u.ac.jp/~itbase/exp/fy2022/fortran_program/inputfromfile_mod.f90>))|URL:http://itpass.scitec.kobe-u.ac.jp/~itbase/exp/fy2023/fortran_program/inputfromfile_mod.f90>))