translitの下請けルーチン ― 2015年01月03日 18:22
まだ紹介していないtranslitの下請けルーチンを紹介します。
まずは、length()。これは、文字列の長さを返します。文字列の最後を示すEOSは含みません。
# length.r4 -- compute length of string integer function length(str) character str(*) for (length = 0; str(length + 1) != EOS; length = length + 1) ; return end
Watcom Fortran77版は、下記の通り。
c length.for -- compute length of string integer function length(str) integer*1 str(*) length = 1 while (str(length) .ne. -2) do ! EOS(-2) length = length + 1 end while length = length - 1 return end
xindex()は次の通りです。フラグallbutに従いiindex()とは裏腹の結果を返します。
# xindex -- invert condition return by iindex integer function xindex(array,c,allbut,lastto) character array(ARB), c integer iindex integer lastto, allbut if (c == EOF) xindex = 0 else if (allbut == NO) xindex = iindex(array,c) else if (iindex(array,c) > 0) xindex = 0 else xindex = lastto + 1 return end
Watcom Fortran77版は、下記の通り。
c xindex -- invert condition return by iindex integer function xindex(array,c,allbut,lastto) integer*1 array(*),c integer allbut integer lastto,iindex if (c .eq. -1) then ! EOF(-1) xindex = 0 else if (allbut .eq. 0) then ! NO(0) xindex = iindex(array,c) else if (iindex(array,c) .gt. 0) then xindex = 0 else xindex = lastto + 1 endif return end
最後に、error()です。これは、メッセージを表示しプログラムを停止させます。 メッセージは、固定文字列として引き渡す仕様になっているため、一工夫が必要です。
Watcom Fortran77版は、メッセージを表示するremark()を使って作りました。
c error.for -- print message s and stop subroutine error(s) character s(*) call remark(s) stop end
c remark.for -- print error message subroutine remark(s) character s(*) ! ARB(*) integer i i = 1 while (s(i) .ne. '.') do call putc(ichar(s(i))) i = i + 1 end while call putc(46) ! PERIOD(46) call putc(10) ! NEWLINE(10) return end
ここまでで、必要なパーツがそろいました。モジュールをビルドしライブラリーに登録し、translitを作成してください。 できましたら、早速テストしてみましょう。
C:\Users\Hiroya\Documents\ratfor\fortran\bat>..\exe\translit abc XYZ abcdefg XYZ XYZdefg XYZ a b c X Y Z X Y Z X Y Z ^Z C:\Users\Hiroya\Documents\ratfor\fortran\bat>..\exe\translit a-z A ABCDEFabcdefg ABCDEFA ABCDEFabcdefgXYZ ABCDEFAXYZ ^Z C:\Users\Hiroya\Documents\ratfor\fortran\bat>..\exe\translit @n # abcd efg hijk ^Z abcd efg#hijk# C:\Users\Hiroya\Documents\ratfor\fortran\bat>
いかがでしょうか。
さて、translitはフィルターです。ほかのプログラムとつなぎ合わせて使うことができます。
次回は、ファイルの処理をするための基礎ルーチンを紹介します。
外部ファイルの結びつけ、fopen(),fclose(),initfile() ― 2015年01月11日 21:05
Watcom Fortran 77では、外部ファイルと措置番号を実行時に結びつけることができます。 しかし、あまり使い勝手がよいとは、限りません。 装置番号1とファイル"ABC.TXT"を結びつけるには、環境変数を設定する必要があります。
set 1=ABC.TXTこの方法だと、使っている装置番号を知っておく必要があります。あまり、賢くないやり方です。 別の方法を考えます。コマンドラインのパラメーターにファイル名を指定する方法を考えてみます。
program ABC.TXTこの方法は便利です。プログラム内部で使用されている装置番号を知る必要はありませんが、 外部ファイルと装置番号結びつける仕組みが必要です。その仕組みがWatcom Fortran 77にあり、それはopen文です。
open(unit=uid, file=fname, action=act, err=99) uid : 装置番号 fname : ファイル名(character型の文字列) act : 'READ','WRITE' 99 : ERRORが起きたときにジャンプする先このままでは、使いにくいので一枚、皮をかぶせます。fopen()です。
c fopen.for -- connect intenal file descripter and external file integer function fopen(uid, fn, act) integer uid integer*1 fn(*), act integer i character*261 cfn ! MAXNAME(261) character*5 cact ! READ WRITE include 'files.fi' if (act .eq. 82) then ! READ(LETR) cact = 'READ' else if (act .eq. 87) then ! WRITE(LETW) cact = 'WRITE' else ! error uid = -1 ! ERR(-1) fopen = -1 ! ERR(-1) return end if call is2cs(fn,cfn,261) ! MAXNAME(261) convert integer string to character string i = 1 while (i .le. 20) do ! MAXFIELS(20) if (finuse(i) .eq. 0) then ! NOUSE(0) open(unit=i, file=cfn, action=cact, err=99) finuse(i) = 1 ! INUSE(1) uid = i fopen = i if (act .eq. 82) then ! READ(LETR) flastc(i) = 81 ! MAXCARD+1(81) fbuf(i,81) = 10 ! NEWLINE(10) fbuf(i,82) = -2 ! EOS(-2) fmode(i) = 82 ! READ(LETR) else if (act .eq. 87) then ! WRITE(LETW) flastc(i) = 0 fmode(i) =87 ! WRITE(LETW) end if return endif i = i + 1 end while 99 continue uid = -1 ! ERR(-1) fopen = -1 ! ERR(-1) return end
includeは、Watcom Fortran 77の機能で、'files.fi'をコンパイル時に、読み込みます。
この中で、is2cs()は、integer*1の文字列をcharacter型の 文字列に変換するものです。
c is2cs -- copy integer string to character string subroutine is2cs(is,cs,maxsiz) integer*1 is(*) character cs(maxsiz) integer maxsiz character char integer i i = 1 ! clear character string while (i .le. maxsiz) do cs(i) = ' ' i = i + 1 end while i = 1 while (is(i) .ne. -2) do ! EOS(-2) if (i .ge. maxsiz) then ! MAXNAME(261) exit end if cs(i) = char(is(i)) i = i + 1 end while return end
files.fiは下記の通りです。
c files.fi -- file interface common valiables common /files/finuse,fbuf,flastc,fmode integer finuse(20) ! inuse flag MAXFILES(20) integer*1 fbuf(20,82) ! I/O buffer MAXFILES(20) MAXLINE(81)+1 integer flastc(20) ! characters in I/O buffer MAXFILES(20) integer*1 fmode(20) ! READ/WIRTE flag MAXFILES(20)
- finuse(i) : 装置番号iが使用中ならばINUSE、そうでなければNOUSE
- fbuf(i,82) : i番目の装置の入出力バッファー
- flastc(i) : i番目の装置の次の読み出し文字位置、または、次の書き出し位置
- fmode(i) : i番目の装置が入出力モード
fopen()の逆で、ファイルを切り離すfclose()を示します。これは、Watcom Fortran 77のclose()に皮をかぶせたものです。 uid=5,6を除外しているのは、標準入力と標準出力だからです。
c fclose.for -- disconnect internal filedescripter and extenal file subroutine fclose(uid) integer uid include 'files.fi' if (.not. ((uid .eq. 5) .or. (uid .eq. 6))) then if (fmode(uid) .eq. 87) then ! WRITE(LETW) call fputc(uid,-1) ! flush buffer by put EOF end if close(unit=uid, status='keep') finuse(uid) = 0 ! NOUSE(0) uid = 0 end if return end
'files.fi'にある変数を、fopen(),fclose()などを使う前に初期化する必要があります。初期化 モジュールinitfile()を作成します。標準入力と標準出力は事前にオープンする必要がないため、 読み出し位置、書き出し位置の初期値をここで設定します。
c initfile.for -- setup file manage array funit subroutine initfile() integer i include 'files.fi' i = 1 while (i .le. 20) do ! MAXFILES(20) finuse(i) = 0 ! NOUSE(0) i = i + 1 end while finuse(5) = 1 ! INUSE(1) for STDIN flastc(5) = 81 ! lastc of read buffer fbuf(5,81) = 10 ! NEWLINE(10) fbuf(5,82) = -2 ! EOS(-2) fmode(5) = 82 ! READ(LETR) finuse(6) = 1 ! INUSE(1) for STDOUT flastc(6) = 0 ! lastc of write buffer fmode(6) = 87 ! WRITE(LETW) return end
これらをコンパイルするのに、fc.batに修正が必要です。includeするファイルをサーチする場所を指定するオプションを 追加します。
@echo off rem fc2.for wfc386 ..\src\%1.for /INCPATH=..\src move ..\bat\%1.obj ..\obj
外部ファイルの入出力、fputc(),fgetc() ― 2015年01月19日 22:30
前回作成したファイルのオープン、クローズルーチンと併せて使用する、ファイル読み書きルーチンを示します。
はじめに、一文字書き出しルーチンfputc()です。putc()を拡張しています。
c fputc.for (extended version) -- put character on file subroutine fputc(u,c) integer i, u integer*1 c include 'files.fi' if (c .eq. -1 .and. flastc(u) .eq. 0) then return ! buffer is empty, nothing to do end if if (flastc(u) .ge. 80 .or. c .eq. 10 .or. c .eq. -1) then ! MAXCARD(80) NEWLINE(10) EOF(-1) write(u,10) (fbuf(u,i),i=1,flastc(u)) 10 format(80a1) ! MAXCARD(80) flastc(u) = 0 end if if (.not. (c .eq. 10 .or. c .eq. -1)) then ! NEWLINE(10) EOF(-1) flastc(u) = flastc(u) + 1 fbuf(u,flastc(u)) = c end if return end
次に、一文字読み込みルーチンfgetc()です。これは、getc()を拡張しています。
c fgetc.for -- (extended version) get character from unit u integer*1 function fgetc(u,c) integer u integer*1 c integer col, i include 'files.fi' flastc(u) = flastc(u) + 1 if (flastc(u) .ge. 80 .or. fbuf(u,flastc(u)) .eq. -2) then ! MAXCARD(80) EOS(-2) read(u,10,end=9) (fbuf(u,i),i=1,80) ! MAXCARD(80) 10 format(80a1) ! MAXCARD(80) flastc(u) = 1 col = 80 while (fbuf(u,col) .eq. 32) do ! BALNK(32) col = col - 1 end while fbuf(u,col+1) = 10 ! NEWLINE(10) fbuf(u,col+2) = -2 ! EOS(-2) endif c = fbuf(u,flastc(u)) fgetc = fbuf(u,flastc(u)) return 9 continue c = -1 ! EOF(-1) fgetc = -1 ! EOF(-1) return end
ここで、問題が生じました。fgetc(5,c)とgetc(c)が交互に呼ばれたらどうなるでしょうか。また、fputc(6,c)とputc(c)が、 交互に呼ばれたらどうなるでしょうか。悲劇が起きます。原因は、どこにあるでしょうか。原因究明は次回に致します。
最近のコメント