テキスト処理

BufferedReader, PrintWriter

Text File の I/O 事例をいくつか紹介します。 で、ここで扱うのは、一行単位で読み込むプログラムの例です(UseBufferedIO)。

今度は、標準出力に結果を表示しながら、sample2.out というファイルも生成します。 出力はこんな感じ(入力ファイルを準備したのは、両大学の統合以前なのでご了承を)。

0 http://en.wikipedia.org/wiki/Osaka_Prefecture_University 大阪府立大学 34.54777908325195 135.5075073242188
1 http://en.wikipedia.org/wiki/Osaka_City_University 大阪市立大学 34.59055709838867 135.5050048828125

こちらはプログラム:

private static void copyfile(String from, String to)
        throws IOException {
    /* File読み込み用Reader */
    BufferedReader in = new BufferedReader(new FileReader(from));
    /* File出力用PrintWriter */
    PrintWriter out = new PrintWriter(to);
    try {
        String line;
        int i = 0;
        while ((line = in.readLine()) != null) { /* 一行read */
            String outline = i + ":" + line; /* 行番号つけて */
            System.out.println(outline); /* 標準出力にprint */
            out.println(outline); /* ファイルにもprint */
            i++;
        }
    } finally {
        in.close();
        out.close(); /* PrintWriter の close()処理 */
    }
}

まず、このプログラムを簡単に解説しておくと、主な登場人物は、以下のふたつ。

入力用 BufferedReader の生成:

  • ファイルを読む場合は、前述どおりファイル名を指定してjava.io.FileReader経由で作成する。
BufferedReader in = new BufferedReader(new FileReader(filename));
  • 標準入力から読む場合は、InputStream→Reader変換をしてあげましょう。
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));

出力用 PrintWriter の生成

  • ファイルを出力する場合は、前述の様にファイル名を指定して作成するだけ。
  • 標準出力に出力する場合は、System.out に対し println(), print() などを呼べばいいでしょう。
    • あるいは、OutputStreamWriter経由で使うこともできます。new PrintWriter(new OutputStreamWriter(System.out)) で変換できます。
    • ファイル入出力と標準入出力を切り替えたければ、java.lang.System クラスの setIn/Out/Err メソッドを使って、標準入出力自身を切り替えるという手もあります。

おまけ情報: java.nio.file.Files は、各種 I/O 用途の static method が備わった便利なクラスです。 newBufferedReader(Path) などのメソッドも提供されているので、こちらを使うのでも構いません。

文字コード

テキスト処理を行う上で、面倒な話を少し。日本語を処理していると文字コードがいろいろあります。

  • UTF8 (演習室で標準設定)
  • EUC-JP
  • ISO-2022-JP, jis
  • MS932 (いわゆる shift JIS。Windows 系で標準的扱い)

FileReaderPrintWriter は、文字コードを意識した処理が可能で、入力ファイルの文字セットを指定出来るようになっています。 指定されていない場合は、システム標準の文字セットで処理を行います。指定する際は、例えば以下のようにすればOKです。

BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(filename), "UTF8"));

後述する java.util.Scanner の場合は、こんな感じ。

Scanner sc = new Scanner(new File(filename), "UTF8");
String line = sc.nextLine();

などとすると良いでしょう。

便利な文字セット指定にJISAutoDetectというのもあります。ISO2022, EUC, Shift JIS などを自動判定します。

語句単位のテキスト処理

次は、java.util.Scanner を使って、語句単位の処理をしてみましょう(UseScanner)。

で、プログラムのコア部分がこちら。

while (in.hasNext()) {
    String url = in.next();
    String name = in.next();
    double lat = in.nextDouble();
    double lon = in.nextDouble();
    System.out.println("名前:" + name + ", 緯度:" + lat +
            ", 経度" + lon + ", Wikipedia URL: " + url);
}

主な利用クラス

  • java.util.Scanner
    • コンストラクタには、file名 や Input Stream を引数として与える。文字セットも指定可能。
    • delimiter(区切り文字)指定: useDelimiter(java.util.regex.Pattern) しない限り、空白相当を切れ目に word に切り分けてくれる
    • next(): 次の word を取得
    • nextInt(): 次の word を int に変換して取得。その他基本データ型用メソッドもあり
    • hasNext(): 次の word の有無を返す。基本データ型版も存在。

ということで、このプログラムの場合、冒頭の url と 大学名を String として取り出し、緯度経度を double で取得しています。 結構便利です。

  • Scanner は、String に対しても利用可能なので、文字列をバラしたいときにもどうぞ。他にも、対象が String であれば、String クラスの split()メソッドや、java.util.StringTokenizer`クラスもあります。

  • これらのクラスは、delimiter の指定に正規表現を用いることもできます。正規表現というのは、文字列の連結, 選択(|),繰り返し(*,+)などを使って、文字列群を表現する方法です。
    例:

    • “,”: “,” (1文字)を区切り文字とする
    • “, “: “, " (2文字の連なり)を区切り文字とする
    • “,| “: “,” もしくは " " を区切り文字とする
    • “,+”: “,” の1文字以上の繰り返し(+) を区切り文字とする
    • “(,| )+”: 「”,” もしくは " “」の1回以上の繰り返し(+) を区切り文字とする
    • “(, )+”: “, " (2文字)の1回以上の繰り返し(+) を区切り文字とする
    • 各種文字クラスの表現は、java.util.regex.Patternの説明を読んでください。空白、アルファベット、数字などをまとめて表現する方法も載っています。
  • 正規表現は、文字列マッチや置換処理にも使えます。

    • String#matches(regex): String が正規表現にマッチするか
    • String#replaceAll(regex, replace): 置換