7. 文字データとファイル入出力 目次へ 7.1. 文字デ−タ 7.1.1. 文字データ宣言 7.1.2. 文字部分列 7.1.3. 代入規則 7.1.4. 文字列の結合 7.1.5. 文字列の大小比較 7.1.6. 文字関数 7.2. ファイル入出力 7.2.1. 入出力文 7.2.2. 入出力ファイルの割当て 7.2.3. その他の入出力関連文 7.1. 文字データ 7.1.1. 文字データ宣言 CHARACTER(LEN=長さ):: 変数などのリスト [90] CHARACTER*長さ:: 変数などのリスト [90] CHARACTER :: 変数*長さ,... [90] CHARACTER 変数*長さ,... CHARACTER*長さ 変数*長さ,... 長さの指定が2重のときは,変数に指定した長さが優先される。 例 CHARACTER(LEN=10):: word, temp CHARACTER :: space*1 = " ", c(2)*5 = (/"ABCDE","VWXYZ"/) CHARACTER*5 :: word, n(0:15)*1 DATA n/'0','1','2','3','4','5','6','7','8','9', & 'A','B','C','D','E','F'/ 注)文字列が副プログラムの仮引数になっており,引用された時点で長さが決まる 場合に,長さを CHARACTER(LEN=*) または 変数*(*) の形で指定する方法があ る。(→例題 9_6の注) 7.1.2. 文字部分列 文字変数(範囲の下限:範囲の上限) 例 CHARACTER:: abc*26 n(2)*5=(/ '12345','67890'/) abc='ABCDEFGHIJKLMNOPQRSTUVWXYZ' のとき abc(1:5), abc(:5) は 'ABCDE' abc(24:26), abc(24:) は 'XYZ' abc(:) は abc そのものと同じ n(1)(2:3) は '23' n(2)(4:4) は '9' abc(1:5:2) , abc(5:1:-1) などは許されていない。 ※ 下限が上限を越えているときには「空文字」となる。 例題 7_1「正の整数の16進数表現を求める。」[ex7_1.f90] ! --- Hexadecimal Notation --- PROGRAM Ex7_1 CHARACTER(LEN=1)::h(0:15) = (/ '0','1','2','3','4','5','6','7' & ,'8','9','A','B','C','D','E','F' /) CHARACTER(LEN=8) :: hex INTEGER :: dec ext:DO hex = " " PRINT*, "Input a Positive Integer, or 0 to stop:" READ *, dec; IF( dec <= 0) EXIT ext hxd: DO i = 0, 7 hex( 8-i : 8-i ) = h( MOD( dec, 16) ) dec = dec/16; IF( dec == 0 ) EXIT hxd END DO hxd PRINT*, hex END DO ext END PROGRAM Ex7_1
(実行例) Input a Positive Integer, or 0 to stop: 16 10 Input a Positive Integer, or 0 to stop: 257 101 Input a Positive Integer, or 0 to stop: 2147483647 7FFFFFFF Input a Positive Integer, or 0 to stop: 0 7.1.3. 代入規則 代入文や入力文で文字変数に代入される文字列が,左辺の文字変数(または文字部分 列,文字配列要素など)に宣言されている長さと比べて 短いとき: 左詰めで代入され,残りには空白文字が代入される。 長いとき: 左詰めで代入され,余った分は切り捨てられる。 代入文の両辺で重なりがあるときの代入結果は,部分配列の代入の場合と同じく 「並列処理」が行われると考えればよい: 例 a='ABCDEFG' のとき a(1:5)=a(2:6) → a は 'BCDEFF' となる a(2:6)=a(1:5) → a は 'AABCDE' となる [90]以前の規格では,後者は 'AAAAAA' となるなど結果は不定であるため,このような アクロバットな使い方は許されていないので,事前に結果を確かめた方がよいであろう。 7.1.4. 文字列の結合 文字列の間の演算として用意されているのは結合(連結)だけである: 文字列1 // 文字列2 例 "ABC"//"DEF" は "ABCDEF" となる。 f='FORTRAN', g='90' のとき f//g は 'Fortran90' となる。 例 9桁以下の整数データの出力を桁数(k),データ数(n)に応じて書式を変える: CHARACTER(LEN=1):: f(9)=(/ '1','2','3','4','5','6','7','8','9' /) CHARACTER :: fmt*20 ・・・・・ fmt = "(2X, 'INT='," // f(n) // "I" // f(k) // ")" PRINT fmt, int ・・・・・ 例えば n=3, k=6 なら,書式仕様 (2X, 'INT=', 3I6) が作られる。 文字変数 fmt の長さは,十分長めに予約しておけばよい。 例題 7_2「異なる4文字からなる英単語を入力して,4文字を置換してできる 全ての語を出力せよ。」[ex7_2.f90] !----- Rearrange Characters of a Word ----- CHARACTER(LEN=5) :: w0, w1, w2, w(1:24) INTEGER :: i, j, k, n = 1 PRINT *, 'Input a word in 4 letters: ' READ '(A)', w0 DO i = 1, 4 w1 = w0(i:i)//w0(1:i-1)//w0(i+1:5) DO j = 2, 4 w2 = w1(1:1)//w1(j:j)//w1(2:j-1)//w1(j+1:5) DO k = 3, 4 w(n) = w2(1:2)//w2(k:k)//w2(3:k-1)//w2(k+1:5) n = n + 1 END DO END DO END DO PRINT '(/(12A5))', w END
文字部分列の範囲指定で,下限が上限を越えているときは,空文字となる。 (実行例) Input a word in 4 letters: read read reda raed rade rdea rdae erad erda eard eadr edra edar ared arde aerd aedr adre ader drea drae dera dear dare daer 7.1.5. 文字列の大小比較 数値データの間の関係式がすべて適用される。 1)長さが異なる文字列の場合,うしろに空白を補充して長さを揃えてから比較する。 2)各文字の大小順序は,どのシステムでも次の関係は共通である: ' ' < 'a' < 'b' < 'c' < ... < 'x' < 'y' < 'z' ' ' はスペース記号 ' ' < 'A' < 'B' < 'C' < ... < 'X' < 'Y' < 'Z' ' ' < '0' < '1' < '2' < ... < '8' < '9' 各群の間の大小関係はシステムによって異なる。 3)空白はすべての文字より小さい。 4)したがってアルファベットのみから成る文字列の大小は辞書順となる。 例 'a' < 'ab' < 'abb' < 'abc' < 'b' < 'ba' 例題 7_3 「入力された英単語の実際の長さを求める。」[ex7_3.f90] ! --- Get the length of a Word --- CHARACTER(LEN=50):: word ! 50字以下とする INTEGER :: len DO PRINT *, "Input a word:" READ '(A50)', word IF( word == "" ) EXIT ! 空行なら終了する DO len = 1, 50 IF( word(len:len) == " ") EXIT ! 最初の空白の位置 END DO PRINT '( "Word length =", I2 )', len-1 END DO END
注)READ文で「A指定」を行っておけば,キーボード入力の際に文字列を「' '」, または「" "」で囲む必要はない。「*指定」の場合でも,「' '」や「" "」を 付けなくてもエラーにはならず気を利かせて解釈してくれることもあるが,文字 列の中に空白やコンマがあるとデータの区切りとみなされ,それ以降は無視され る。 (実行例) Input a word: Fortran90 Word length = 9 Input a word: It's really complicated! Word length = 4 ← 最初のIt'sだけの長さになっている Input a word: 改行で終了 例題 7_4「英単語をいくつか入力し,辞書順に並べ替える。単語の長さは 高々15字,入力する単語数は100以下とせよ。」[ex7_4.f90] ! --- Bubble Sorting --- PROGRAM Ex7_3_Bubble_Method CHARACTER(LEN=15) :: word(100), wtemp INTEGER :: num, n, m DO num = 1, 100 PRINT*, 'Input a word:' READ '(A)', wtemp; IF( wtemp == "") EXIT WORD(num) = wtemp END DO num = num-1 DO n = num, 2,-1 DO m = 1, n-1 IF( word(m) > word(m+1) ) THEN wtemp = word(m) word(m) = word(m+1) word(m+1) = wtemp END IF END DO END DO DO n = 1, num PRINT*, word(n) END DO END PROGRAM Ex7_3_Bubble_Method
注)「バブル法」 隣り合う2つを比べ,大きさ順が逆であれば入れ替えるという 操作を繰り返せばよい。 7.1.6. 文字関数 例題 7_1のような操作は,ちゃんと組込み関数で用意されている: INDEX(s1,s2) 文字列s1の中で文字列s2が最初に現れた先頭位置(なければ0) LEN_TRIM(s) 文字列sの右の余分な空白(途中の空白ではない)を除いた長さ 例 LEN_TRIM('I am a student.') は 15 (その他) ADJUSTL(s) 文字列sを左詰にする ADJUSTR(s) 同 右詰にする CHAR(I) システムコード(整数)に対応する文字を求める ICHAR(a) 文字aのコード(整数)を求める LEN(s) 文字列sの長さ(予約されている長さ) LGE(s1,s2) 文字列s1とs2の辞書順の大小関係(論理型) LGT(s1,s2) 同 LLE(s1,s2) 同 LLT(s1,s2) 同 REPEAT(s,n) 文字列sをn回複写して連結した文字列を作る SCAN(s1,s2[,BACK]) 文字列s1の中に文字列s2に含まれる文字があるときには,その先頭 位置,なければ0(論理型引数 BACK に真の値が指定されていると, うしろから検索,例 BACK=.TRUE.) VERIFY(s1,s2[,BACK]) 文字列s1の中に,文字列s2に含まれない文字が見つかれば,その先頭 位置,なければ0(同じくBACKが真のとき,後ろから検索) TRIM(s) 文字列sの右の空白を除去した文字列を作る 例題 7_5「英文を入力し,文字の使用頻度を数え,多い順に使用頻度を出力 する。途中のスペースも文字とし,大文字・小文字は区別しない。」[ex7_5.f90] ! --- Counting A, B, C ,... --- CHARACTER :: abc*53, input*80 INTEGER :: freq(0:27) = 0, code(255) = 0, i, ichi LOGICAL :: mask(0:27) abc=" ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" ! --- Make a Code Table --- 空白とアルファベット以外は code=0 とする DO ichi = 1, 53 IF(ichi <= 27) THEN code( ICHAR( abc(ichi:ichi) ) ) = ichi ELSE code( ICHAR( abc(ichi:ichi) ) ) = ichi-26 ! 小文字の処理 END IF END DO ! --- Sentence Input and Count Frequency of each Characters --- DO PRINT*, "Write a sentence shorter than 80 characters:" READ '(A)', input; IF( input == "" ) EXIT DO i = 1, LEN_TRIM(input) ichi = code( ICHAR( input(i:i) ) ) freq(ichi) = freq(ichi) + 1 END DO END DO ! --- Array Sorting with use of Mask --- freq(0) = 0; mask = ( freq > 0 ) ! 論理配列式とその代入 DO WHILE( COUNT(mask) > 0 ) ! freq > 0 での最大値を調べる ichi = SUM( MAXLOC(freq, mask))-1 ! MAXLOC は要素数1の配列(注) PRINT '(4X, A1, ":", I5)', abc(ichi:ichi), freq(ichi) mask(ichi) = .FALSE. END DO PRINT '("Total=",I5)', SUM( freq ) END
(※)「配列freqの各要素が正か?を問う関係式」を要素とする論理型配列の配列式 注)組込み関数 MAXLOC は,引数が1次元配列であっても「要素数1の配列」が結果 として返される(→6章6-5の注)ため,直接スカラ変数 に代入することができない。これをスカラに変換するために要素の和(今の場合 1個の和)を返す関数 SUM を用いてある。なお,MAXLOCの返す位置は添え字の 値そのものではなく,先頭からの相対位置である。例えば a(0:3)=(/ 1, 4, 3, 0 /) の場合,最大値 4 の相対位置である (/ 2 /) が返される。したがって, a(MAXLOC(a)) は 3 となる。 7.2. ファイル入出力 7.2.1. 入出力文 コンソール入出力文 PRINT 書式識別子 [,出力リスト] READ 書式識別子,入力リスト ・書式識別子は,*印,編集記述子のならびを書いた文字定数(または それを内容とする文字変数名),またはFORMAT文の文番号 ・出力リストのないPRINT文では,改行が行われる。 ファイル指定入出力文 WRITE([UNIT=]装置番号,[FMT=]書式識別子[,制御項目]) [出力リスト] READ([UNIT=]装置番号,[FMT=]書式識別子[,制御項目]) 入力リスト ・UNIT=,FMT=を省略する場合は両方とも省略し,装置番号,書式識別子を 書く順番は上のとおりにしなければならない。 ・装置番号 * : コンソール入出力 (WRITE(*,*) は PRINT* と同じ) 整数 : OPEN文で割当てられた入出力用ファイル番号 5 : OPEN文による割当がなければコンソール入力 6 : 同 コンソール出力 文字変数:プログラム中で使われている文字変数に読み書きできる。 「内部ファイルの方法」といい,必ず書式を指定すること。 ・制御項目の主なもの IOSTAT = 整変数i 正常に処理されたとき :i=0 エラーが発生したとき :i>0 ファイルが終了したとき:i<0 ERR = 文番号 エラーが発生すると文番号の文へ実行を移す END = 文番号 ファイルが終了すると文番号の文へ実行を移す ADVANCE = 'no' 入出力のあと改行しない。編集記述子$と同じ効果。 例 f = '(A10, F10.5)' ; WRITE(6, f) x WRITE(*,'(A10, F10.5)') x PRINT '(A10, F10.5)', x READ(*, 100) i, j, k; 100 FORMAT(3I10) READ '(3I10)', i, j, k f ='(3I10)'; READ(5,f) i, j, k READ(8, '(I10)', IOSTAT=i) k; IF(i < 0) GOTO 999 READ(8, '(I10)', END= 999) k 書式なし入出力文 上の,FMT=書式識別子 のないもの 注)*指定はデフォルト書式を指定するもので,書式なしではない。 7.2.2. 入出力ファイルの割当て OPEN文: 入出力用ファイル名を装置番号と関係づける。 OPEN([UNIT=]装置番号,FILE=ファイル名 [,制御項目] ) ・UNIT= を省略するときは,装置番号を( )の中の先頭に書く ・装置番号 整数データ(整定数,整変数など)で与える ・ファイル名 入出力に用いるファイル名を文字型データとして与える ・制御項目の主なもの IOSTAT = 整変数i 正常に処理されたとき :i=0 エラーが発生したとき :i>0 ファイルが終了したとき:i<0 ERR = 文番号 エラーが発生すると文番号の文へ実行を移す CLOSE文: OPEN文で関係づけられていたファイルを閉じる。ファイルは閉じて おかないと次に使用することができない。 CLOSE([UNIT=]装置番号 [,制御項目] ) ・制御項目の主なもの IOSTAT = 整変数i 正常に処理されたとき :i=0 エラーが発生したとき :i>0 ファイルが終了したとき:i<0 ERR = 文番号 エラーが発生すると文番号の文へ実行を移す STATUS = 'DELETE' 終了後ファイルを消去する。 途中で用いる配列が大きすぎてシステムのメモリ が足りず実行できないような場合に,一時的に ファイルとしてハードディスクに書き込む,と いうような使い方が可能である。 注)CLOSE文がなくても,プログラムの実行終了で自動的に閉じられるが,途中で プログラムエラーが発生して正常終了しなかった場合には開いたまま残るので, できるだけCLOSE文で閉じさせるようにする。 例 新しいファイル result.txt に結果を書き込む: OPEN(8, FILE='result.txt') WRITE(8, '(8F10.7)') (x(i), i=1, 8) CLOSE(8) ファイル D:\mydocument\report.txt から文章を読み込む: OPEN(11, FILE='D:\mydocument\report.txt') DO j = 1, 1000 READ(11, '(A)', IOSTAT = io ) line(j) IF( io /= 0 ) EXIT END DO CLOSE(11) 次のように書いてもよい: ................ READ(11, '(A)', END=999 ) line(j) END DO 999 CLOSE(11) 注)入出力ファイルがプログラムコンパイル後の実行ファイルと同じディレクトリ (フォルダ)にある場合(作る場合)は,前の例のように書けばよいが,そうで ないときには後の例のようにディレクトリまで指定しておかなければならない。 この場合,実行ファイルになってしまうと,設定の異なるコンピュータでは使え なくなる。これを避けたければ,ファイル名を文字変数として会話的に与える, などの方法をとればよい。 例 CHARACTER(LEN=40) :: file_name PRINT*,"File Name for Output data?" READ '(A)', file_name ! A指定すれば入力のとき" "は不要 OPEN(8, FILE = TRIM(file_name)) ! 右の余分な空白は除去しておく。 WRITE(8, '(10I4)') (n**2, n=2, 20, 2) CLOSE(8) END 例題 7_6 「コード番号32〜255の文字の表を,32行7列で作表し,ファイル 'Code_Table'に出力する。」[ex7_6.f90] !----- Character Code Table ----- INTEGER :: i, j OPEN(8, FILE='Code_Table') DO i = 0, 31 WRITE(8, "(7(2X, I3, 2X, A))") (i+32*j, CHAR(i+32*j), j = 1, 7) END DO CLOSE(8) END
7.2.3. その他の入出力関連文 BACKSPACE(装置番号) :一つ前の記録の先頭に戻る ENDFILE(装置番号) :「ファイル終了記録」を書き出す REWIND(装置番号) :ファイルの先頭にもどる INQUIRE(制御項目) :装置番号(またはファイル名)で指定されたファイル の設定状況を調べる。(略) ⇒7章先頭へ ⇒8章へ