package kc; import java.nio.file.*; import java.io.*; /** * ソースファイルを開き, 一文字ずつ切り出してLexicalAnalyzer に渡す働きをするクラス. */ class FileScanner { private BufferedReader sourceFile; // 入力ファイルの参照 private String line; // 行バッファ private int lineNumber; // 行カウンタ private int columnNumber; // 列カウンタ private char currentCharacter; // 読みとり文字 private char nextCharacter; // 先読み文字 /** * 引数 sourceFileName で指定されたファイルを開き, sourceFile で参照する. * 教科書 p. 210 ソースコード 10.1 ではtry-with-resources 文を用いてファイルの * 参照と読み取りを一度に行っているが,このコンストラクタではファイルの参照 * だけを行う. * また lineNumber, columnNumber, currentCharacter, nextCharacter を初期化する * @param sourceFileName ソースプログラムのファイル名 */ FileScanner (String sourceFileName) { Path path = Paths.get (sourceFileName); // ファイルのオープン try { sourceFile = Files.newBufferedReader (path); } catch (IOException err_mes) { System.out.println (err_mes); System.exit (1); } // 各フィールドの初期化 lineNumber = 0; columnNumber = -1; nextCharacter = '\n'; nextChar(); // nextCharによって nextCharacter に先頭文字を読み込む } /** * sourceFileで参照しているファイルを閉じる */ void closeFile() { try { sourceFile.close(); } catch (IOException err_mes) { System.out.println (err_mes); System.exit (1); } } /** * sourceFile で参照しているファイルから一行読み, フィールド line(文字列変数) にその行を格納する * 教科書 p. 210 ソースコード 10.1 では while文で全行を読み取っているが,このメソッド内では * while文は使わず1行だけ読み取りフィールドline に格納する. */ void readNextLine() { try { if (sourceFile.ready()) { // sourceFile中に未読の行があるかを確認 (例外:IllegalStateException) /* * readLineメソッドでsourceFileから1行読み出し 読み出された文字列は改行コードを含まないので * 改めて改行コードをつけ直す */ line = sourceFile.readLine() + '\n'; } else { line = null; } } catch (IOException err_mes) { // 例外は Exception でキャッチしてもいい // ファイルの読み出しエラーが発生したときの処理 System.out.println (err_mes); closeFile(); System.exit (1); } } /** * 一文字先読みするためのメソッド(nextCharacter のためのgetter) * @return nextCharacter の内容 */ char lookAhead() { return nextCharacter; } /** * 現在読んでいる行の内容を返すメソッド(line のためのgetter) * @return line の内容 */ String getLine() { return line; } /** * 一文字切り出し用のメソッド,ファイル末に達していたら'\0'を返す * @return 切り出した文字 */ char nextChar() { currentCharacter = nextCharacter; if (nextCharacter == '\0') { // ファイル末なら共通動作以外何もしない } else if (nextCharacter == '\n') { // 行末の場合 readNextLine(); // 次の行を読む if (line != null) { // 読んだ行が null でない場合 nextCharacter = line.charAt (0); ++lineNumber; columnNumber = 0; } else { // 読んだ行が null(ファイル末) の場合 nextCharacter = '\0'; // '\0'はファイル末を表す } } else { // ファイル末でも行末でもない場合, nextCharacter = line.charAt (++columnNumber); } return currentCharacter; } /** * ソースファイルのどの部分をスキャンしているのかを表す文字列を返す * @return 表示文字列 */ String scanAt() { String message = lineNumber + "行目\n" + line; for (int i = 0; i < columnNumber - 1; ++i) message += " "; message += "*\n"; return message; } public static void main (String args[]) { char nextChar; /* * このクラスのインスタンスを作成し, sourceFileScanner という変数 でそのインスタンスを参照する. * 下記では,インスタンス作成の際 bsort.k というファイルを開く */ FileScanner sourceFileScanner = new FileScanner ("bsort.k"); // "bsort.k" の代わりに args[0] にすると実行時に引数で指定したファイルを開ける // 問題2.6用のファイル出力部分 while ((nextChar = sourceFileScanner.nextChar()) != '\0') { System.out.print(nextChar); } // 問題2.5用のファイル出力部分 // do { // sourceFileScanner.readNextLine(); // if (sourceFileScanner.getLine()==null) break; // System.out.print(sourceFileScanner.getLine()); // } while (true); sourceFileScanner.closeFile(); } }