Modula-2でプログラミング -- 文字列探索プログラム find の制作(その1) ― 2024年12月28日 21:00
指定された文字列を 指定されたファイルの中から探し出すプログラム find を紹介します。 このように使います。
C0#find arguments>GetLine *.def *.mod C:STDFILEI.DEF 6: GetLine; C:STDFILEI.DEF 8:PROCEDURE GetLine(VAR pFileIO: pFileIOStruct; C:STDFILEI.MOD 10:PROCEDURE GetLine(VAR pFileIO: pFileIOStruct; C:STDFILEI.MOD 36:END GetLine; C:FIND.MOD 8:FROM StdFileIO IMPORT GetLine; C:FIND.MOD 71: Chars := GetLine(pFileIO,Line); 63K QP/M VER 2.7 (BIOS VER 1.0) (C) 1988.12.1. K.YOSHIDA 2018.10.28. MODEFIED BY H.KIDA C0#find arguments>GetChar *.def *.mod C:BASICFIL.DEF 7: ReadFile,GetChar; C:BASICFIL.DEF 28:PROCEDURE GetChar(VAR pFileIO: pFileIOStruct): CHAR; C:STDFILEI.MOD 3: pFileIOStruct,GetChar; C:STDFILEI.MOD 20: C := GetChar(pFileIO); C:BASICFIL.MOD 65:PROCEDURE GetChar(VAR pFileIO: pFileIOStruct): CHAR; C:BASICFIL.MOD 89:END GetChar; 63K QP/M VER 2.7 (BIOS VER 1.0) (C) 1988.12.1. K.YOSHIDA 2018.10.28. MODEFIED BY H.KIDA C0#
find は、ファイルを読みますので、ファイルアクセスのルーチンが必要になります。 Bdosを直接使うことができるので、かんたんなファイル読み込みモジュール BasicFileIO を作成しました。 更に、行単位のファイル読み出しが必要なので、上位のモジュール StdFileIO を作成しました。StdFileIOは、 Bdosとは、独立しています。
まず、 BasicFileIO です。基本的なファイルアクセスルーチンの集まりです。 今回は、読み出せれば良いので、書き込み関係のルーチンは作成していません。基本的なファイルのアクセスは、 一文字単位です、 定義モジュールは、以下のとおりです。
DEFINITION MODULE BasicFileIO; FROM OpSys IMPORT FCB,FCBFileName; EXPORT QUALIFIED FileIOStatus,FileIOStruct,pFileIOStruct, OpenFile,CloseFile,EOFFile, ReadFile,GetChar; CONST DMASize = 128; TYPE FileIOStatus = (Success,InvalidFileName, OpenError,CloseError,IOError); DMA = ARRAY [0..DMASize-1] OF CHAR; pFileIOStruct = POINTER TO FileIOStruct; FileIOStruct = RECORD FCBBuf: FCB; DMABuf: DMA; ReadPos: CARDINAL; WritePos: CARDINAL; EOFFlag: BOOLEAN; END; PROCEDURE OpenFile(FileName: ARRAY OF CHAR; VAR pFileIO: pFileIOStruct): FileIOStatus; PROCEDURE CloseFile(VAR pFileIO: pFileIOStruct): FileIOStatus; PROCEDURE ReadFile(VAR pFileIO: pFileIOStruct): FileIOStatus; PROCEDURE GetChar(VAR pFileIO: pFileIOStruct): CHAR; PROCEDURE EOFFile(pFileIO: pFileIOStruct): BOOLEAN; END BasicFileIO.
実装モジュールは、以下のとおりです。手続き GetChar では、CR LF と連続した場合、 LF を読み飛ばします。
IMPLEMENTATION MODULE BasicFileIO; FROM InOut IMPORT Write,WriteString,WriteLn; FROM OpSys IMPORT BdosFunctions,FCBFileName,Bdos,FCB; FROM FileNames IMPORT StrToFCB,NameState; FROM SYSTEM IMPORT ALLOCATE,ADR,TSIZE,WORD; CONST CR = 15C; LF = 12C; EOFMARK = 32C; PROCEDURE OpenFile(FileName: ARRAY OF CHAR; VAR pFileIO: pFileIOStruct): FileIOStatus; VAR BdosRc: CARDINAL; Junk: WORD; BEGIN (* Allocate FCB and DMA / pre-Set *) ALLOCATE(pFileIO,TSIZE(FileIOStruct)); pFileIO^.FCBBuf.name.text := ''; pFileIO^.FCBBuf.rest := ''; pFileIO^.DMABuf := ''; IF StrToFCB(FileName,pFileIO^.FCBBuf.name) # NameOK THEN RETURN InvalidFileName END; Bdos(setDMA,ADR(pFileIO^.DMABuf),Junk); Bdos(openF,ADR(pFileIO^.FCBBuf),BdosRc); IF BdosRc # 255 THEN pFileIO^.ReadPos := DMASize; pFileIO^.WritePos := 0; pFileIO^.EOFFlag := FALSE; RETURN Success ELSE RETURN OpenError END END OpenFile; PROCEDURE CloseFile(VAR pFileIO: pFileIOStruct): FileIOStatus; VAR BdosRc: CARDINAL; BEGIN Bdos(closeF,ADR(pFileIO^.FCBBuf),BdosRc); IF BdosRc = 0 THEN RETURN Success ELSE RETURN CloseError END END CloseFile; PROCEDURE ReadFile(VAR pFileIO: pFileIOStruct): FileIOStatus; VAR BdosRc: CARDINAL; BEGIN Bdos(readSeq,ADR(pFileIO^.FCBBuf),BdosRc); IF BdosRc = 0 THEN RETURN Success ELSE RETURN IOError END END ReadFile; PROCEDURE GetChar(VAR pFileIO: pFileIOStruct): CHAR; VAR C: CHAR; BEGIN IF DMASize = pFileIO^.ReadPos THEN IF ReadFile(pFileIO) = Success THEN pFileIO^.ReadPos := 0 ELSE pFileIO^.ReadPos := 0; pFileIO^.DMABuf[pFileIO^.ReadPos] := EOFMARK END END; C := pFileIO^.DMABuf[pFileIO^.ReadPos]; IF C = CR THEN IF pFileIO^.DMABuf[pFileIO^.ReadPos+1] = LF THEN INC(pFileIO^.ReadPos) END END; INC(pFileIO^.ReadPos); IF C = EOFMARK THEN pFileIO^.EOFFlag := TRUE END; RETURN C END GetChar; PROCEDURE EOFFile(pFileIO: pFileIOStruct): BOOLEAN; BEGIN RETURN pFileIO^.EOFFlag END EOFFile; END BasicFileIO.
GetChar で、 LF を読み飛ばす以外は、面倒くさいところは、ないでしょう。
この、 BasicFileIO モジュールをつかって、行単位の読み出しルーチンを作ってます。 StdFileIO モジュールです。 このモジュールは、 CP/M のファイル構造と独立です。必要最低限の行単位の読み出ししか作成していません。
定義モジュールは、以下の通り。
DEFINITION MODULE StdFileIO; FROM BasicFileIO IMPORT pFileIOStruct; EXPORT QUALIFIED GetLine; PROCEDURE GetLine(VAR pFileIO: pFileIOStruct; VAR LineBuffer: ARRAY OF CHAR): CARDINAL; END StdFileIO.
実装モジュールは、以下の通りです。
IMPLEMENTATION MODULE StdFileIO; FROM BasicFileIO IMPORT pFileIOStruct,GetChar; CONST EOS = 0C; EOFMARK = 32C; CR = 15C; PROCEDURE GetLine(VAR pFileIO: pFileIOStruct; VAR LineBuffer: ARRAY OF CHAR): CARDINAL; VAR C: CHAR; CPos: CARDINAL; BEGIN CPos := 0; LOOP C := GetChar(pFileIO); CASE C OF EOFMARK: LineBuffer[CPos] := EOS; RETURN CPos | CR: LineBuffer[CPos] := EOS; RETURN CPos END; LineBuffer[CPos] := C; INC(CPos); IF CPos = HIGH(LineBuffer) THEN LineBuffer[CPos] := EOS; RETURN CPos END END END GetLine; END StdFileIO.
GetLine は、一文字読み出しを繰り返し、 CR または EOF が来たら、その一文字前の文字までを一行とします。 また、バッファーのサイズ以上に読み取ろうとした場合は、強制的に一行にしてしまいます。溢れてしまうと困りますので。
find 本体の紹介は次回にします。
Modula-2 でプログラミング -- 文字列探索プログラム find の製作 (その2) ― 2024年12月28日 21:13
BasicFileIO モジュール、StdFileIO モジュールをつかった find プログラムは、以下のとおりです。
MODULE Find; FROM InOut IMPORT Write,WriteCard,WriteString,WriteLn; FROM Files IMPORT FILE,FileState,Open,Close,Read; FROM CmdArgs IMPORT Argc,Argv; FROM FileSearch IMPORT pFileList,FileNameNode,MakeFileList; FROM BasicFileIO IMPORT FileIOStruct,pFileIOStruct, OpenFile,CloseFile,EOFFile,FileIOStatus; FROM StdFileIO IMPORT GetLine; CONST EOS = 0C; MAXLINE = 128; FILENAMESIZE = 14; EOFMARK = 32C; VAR FileMatch: ARRAY [0..FILENAMESIZE] OF CHAR; Target: ARRAY [0..MAXLINE] OF CHAR; CPos,FileNumber,LineNumber,nArgc: CARDINAL; pList: pFileList; PROCEDURE putLine(FileName:ARRAY OF CHAR;Line:CARDINAL; Buffer: ARRAY OF CHAR); BEGIN WriteString(FileName); Write(' ');WriteCard(Line,1);Write(':'); WriteString(Buffer); WriteLn END putLine; PROCEDURE findTarget(Target,Line:ARRAY OF CHAR): BOOLEAN; VAR t,l: CARDINAL; found: BOOLEAN; BEGIN found := FALSE; t := 0; l := 0; WHILE (Line[l] # EOS) AND (NOT found) DO IF Line[l] = Target[0] THEN t := 1; WHILE Line[l+t] = Target[t] DO INC(t); END; IF Target[t] = EOS THEN found := TRUE; END; END; INC(l); END; RETURN found END findTarget; PROCEDURE Usage(); BEGIN WriteString('find');WriteLn; WriteString(' TargetString File1 File2 FIle3,,,,');WriteLn END Usage; PROCEDURE MatchLines(FileName,TargetString: ARRAY OF CHAR); VAR pFileIO: pFileIOStruct; Line: ARRAY [0..MAXLINE-1] OF CHAR; Chars: CARDINAL; NLines: CARDINAL; Junk: FileIOStatus; BEGIN IF OpenFile(FileName,pFileIO) = Success THEN NLines := 1; WHILE NOT EOFFile(pFileIO) DO Chars := GetLine(pFileIO,Line); IF findTarget(TargetString,Line) THEN putLine(FileName,NLines,Line); END; INC(NLines); END; Junk := CloseFile(pFileIO) END END MatchLines; BEGIN nArgc := Argc(); IF nArgc < 1 THEN Usage(); HALT END; Argv(0,Target); FileNumber := 1; WHILE FileNumber < nArgc DO Argv(FileNumber,FileMatch); pList := MakeFileList(FileMatch); WHILE pList # NIL DO MatchLines(pList^.FileName,Target); pList := pList^.Next END; INC(FileNumber) END END Find.
MachLine で、指定された文字列が読み込んだ行に存在するかを検査し、存在したら書き出します。
ファイル名のリスト構造が丸見えなのでひと工夫する必要がありますが、今回は見送りました。
最近のコメント