Aritalab:Lecture/Programming/Java/Grep

From Metabolomics.JP
Jump to: navigation, search

Javaにおける文字列処理

ここでは、Javaのページで解説した以下のサンプルプログラムを改良してみましょう。

ファイルから1行ずつ読み込んで正規表現検索をする Grep.java (これはJavaのページと同じ)
import java.io.*;         // ioライブラリの全クラスを使えるようにする。
import java.util.regex.*;

public class Grep {
  public static void main(String[] args)
    {
      if (args.length < 2)
        { System.err.println("Usage: Grep pattern file"); 
          System.exit(1);
        }
      try {
        Pattern p = Pattern.compile(args[0]); // java.util.regexで定義される正規表現
        BufferedReader br= new BufferedReader
          (new FileReader(args[1]));          // java.ioで定義されるクラス
        String line;
        while ((line = br.readLine()) != null)
          if (p.matcher(line).find())
            System.out.println(line);
      } catch (Exception e) { e.printStackTrace(); }
    }
}

このプログラムは第一引数に正規表現パターン、第二引数にファイル名を取るだけですが、まず複数ファイルを指定できるようにします。ファイルを開いて検索する部分の外側に for ループを追加します。正規表現パターンのコンパイルは1回しか行わないところに注意して下さい。

        for(int i=1; i < args.length; i++) {
          BufferedReader br= new BufferedReader
            (new FileReader(args[i]));          // java.ioで定義されるクラス
          String line;
          int cnt = 0;
          while ((line = br.readLine()) != null) {
            cnt++;
            if (p.matcher(line).find())
              System.out.println(args[i] + cnt + ": " + line);
          }
        }

Java では配列の長さを args.length のように指定できるので大変便利です。上の例ではファイルに新しくアクセスするたびにカウンター cnt を 0 にして行数を数え、ファイル名と行数の後にヒット行を出力しています。

文字列を扱う際の注意

Java の String クラスは大変便利です。例えば

String pi = "pi = ";
String s = pi + 3.1415;

と書くだけで s = "pi = 3.1415" という文字列を作成してくれます。しかし文字列を変更するたびに新しい String クラスが作成されている点に注意しましょう。文字を連結するだけで

  1. StringBuffer というバッファークラスを作成する
  2. このクラスに、+ で繋がる String クラスの中身(上の例では pi と3.1415に対応する文字列)をコピーする
  3. StringBuffer の中身を新しい String クラス (上の例では s ) にコピーする

という処理が行われています。便利な半面、C言語の strcpy などに比べるとコピー回数の2倍を遥かに超える手間がかかります。

このため、上に示した汎用の Grep プログラムで行をまたぐパターンにマッチさせるのは面倒な作業です。以下のようにパラグラフ単位で文字列検索を行えば良いように思えますが、文字列のコピーが多すぎて動作が遅くなります。

  : 書いてはいけないプログラム例
  while (true) {
    String paragraph = "";
    while ((line = br.readLine()) != null) {
      if (line.equals("")) // 空行ならbreak
        break;
      paragraph += line;
      
    }
    // パラグラフ中にパターンを探す
    if (p.matcher(paragraph).find())
      System.out.println(paragraph);
    if (line == null)
      break;
  }
  :

このプログラムでは paragraph = paragraph + line; という処理が入力ファイルに対して行います。空行を含まないファイルに適用すると、中身全てを1個の文字列として得るまでコピーを繰り返すことになります。これでは汎用とはいえません。

汎用性を諦めるか、パターンマッチを自分で用意することを奨めます。

Personal tools
Namespaces

Variants
Actions
Navigation
metabolites
Toolbox