Javaで正規表現
先月末に正規表現の勉強会をしました(http://dev.ariel-networks.com/articles/workshop/regex/)。
良い内容だと思っているのですが、いまいち盛り上がりませんでした。 後日、あるベテランプログラマーに、「Perlなんて言うからダメです。今時の若者はPerlなんて使いません」とたしなめられました。 普段、ぼくはPerlよりsedやawkを使う方が多いのですが、sedやawkでは話が通じないと思い、Perlに日和ったつもりでした。判断が甘かったようです。
今月も続きをやる予定です。 本当はNFAとDFAの話をしたいのですが、また受けが悪そうなので、その辺はさらっと流して、Javaで使う正規表現を全面に押すつもりです。
Java1.4以降、java.util.regexとして標準パッケージで正規表現が使えます。 もうひとつ有名なのはJakartaのregexpです(http://jakarta.apache.org/regexp/index.html)。 その他、利用可能な正規表現の実装は http://regex.info/java.html にまとまっています。
java.util.regexのサンプルコードです。 引数で正規表現のパターンを渡して、標準入力から入力テキストを渡します。
// java5 standard regex test
import java.io.*;
import java.util.*;
import java.util.regex.*;
public class MyRegex {
    public void doit(String pattern) {
        Pattern regex = Pattern.compile(pattern);
        BufferedReader fin = new BufferedReader(new InputStreamReader(System.in));
        try {
            while (true) {
                System.out.println("input any text");
                String s = fin.readLine();
                Matcher m = regex.matcher(s);
                if (m.matches()) {
                    int count = m.groupCount();
                    System.out.format("count = %d\n", count);
                    for (int i = 1; i <= count; i++) {
                        System.out.format("group: %s\n", m.group(i));
                    }
                }
            }
        } catch (IOException e) {
            ;
        }
    }
    
    public static void main(String argv[]) {
        MyRegex re = new MyRegex();
        if (argv.length != 1) {
            System.err.println("need one argument. regex-pattern");
            System.exit(0);
        }
        re.doit(argv[0]);
    }
}
次はJakartaのregexpのサンプルコードです。動作は同じです。
// jakarta regexp test
import java.io.*;
import java.util.*;
import org.apache.regexp.*;
public class MyRegexp {
    public void doit(String pattern) {
        RE regex = new RE(pattern);
        BufferedReader fin = new BufferedReader(new InputStreamReader(System.in));
        try {
            while (true) {
                System.out.println("input any text");
                String s = fin.readLine();
                if (regex.match(s)) {
                    int count = regex.getParenCount();
                    System.out.format("count = %d\n", count);
                    for (int i = 1; i < count; i++) {
                        System.out.format("group: %s\n", regex.getParen(i));
                    }
                }
            }
        } catch (IOException e) {
            ;
        }
    }
    
    public static void main(String argv[]) {
        MyRegexp re = new MyRegexp();
        if (argv.length != 1) {
            System.err.println("need one argument. regex-pattern");
            System.exit(0);
        }
        re.doit(argv[0]);
    }
}
どちらも実装はNFAです。 「詳説 正規表現」ではNFAとDFAを見分けられる(可能性が高い)パターン例が載っています。 NFAだとバックトラックの嵐で異常に時間がかかるのに対し、DFAではすぐに終わるパターンです。
次のようなパターンが載っています。
- 正規表現パターン: X(.+)+X
- 入力テキスト: =XX===========================================================
実装がNFAであれば、無限に近いぐらいCPUが占有される(可能性が高い)、と書かれています。
まずperl, sed, grepで確認してみましたが、どれもすぐに終わります。perlとsedは刺さっても良さそうなものですが。何か細工があるのでしょうか。
Emacsを試すと、見事に刺さりました(isearch-forward-regexp)。
次のPythonも試してみました。これも刺さりました。
#!/usr/bin/python
import re
input = '=XX=========================================================================='
m = re.compile('X(.+)+X').search(input)
print input[m.start(1):m.end(1)]
さて、Javaの番です。 java.util.regexは、すぐに返ってきました。実装はNFAのはずですが(ソースを見て確認済み)、回避しています。さすが、我らがJavaです。
しかし、Jakartaのregexpは刺さりました。そんなものです。
- Category(s)
- カテゴリなし
- The URL to Trackback this entry is:
- http://dev.ariel-networks.com/Members/inoue/java-regex/tbping
 
 
             
            