java.util.regex-Pattern.compile()の重要性?


118

Pattern.compile()メソッドの重要性は何ですか?オブジェクトを
取得する前に正規表現文字列をコンパイルする必要があるのはなぜMatcherですか?

例えば ​​:

String regex = "((\\S+)\\s*some\\s*";

Pattern pattern = Pattern.compile(regex); // why do I need to compile
Matcher matcher = pattern.matcher(text);

2
まあ、実装(JDK 1.7など)が新しいPattern(regex、0)への単なるショートカットである場合、その重要性はほとんどありません。とはいえ、REALの重要性は静的メソッド自体ではなく、後で使用するために保存できる新しいパターンの作成と返却です。静的メソッドが新しいルートを取り、Patternオブジェクトをキャッシュする他の実装があるかもしれません。それは、Pattern.compile()の重要性の実際のケースです!
marcolopes 14

答えは、パターンと一致するクラスを分離することの重要性を強調します(これはおそらく質問が尋ねるものです)new Pattern(regex)が、静的コンパイル関数の代わりにコンストラクターを使用できない理由はだれにもわかりません。marcolopesのコメントはその場です。
kon psych

回答:


144

このcompile()メソッドは常にある時点で呼び出されます。これは、Patternオブジェクトを作成する唯一の方法です。だから問題は本当に、なぜあなたはそれを明示的に呼ぶ必要があるのですか?1つの理由は、Matcherオブジェクトへの参照が必要なgroup(int)ため、キャプチャグループのコンテンツを取得するなど、そのメソッドを使用できるためです。Matcherオブジェクトを取得する唯一の方法は、Patternオブジェクトのmatcher()メソッドを使用することであり、Patternオブジェクトを取得する唯一の方法は、compile()メソッドを使用することです。次にfind()、とは異なりmatches()、StringクラスまたはPatternクラスで複製されないメソッドがあります。

もう1つの理由は、同じPatternオブジェクトを何度も作成しないようにするためです。Stringの正規表現を利用したメソッド(またはmatches()Pattern の静的メソッド)を使用するたびに、新しいパターンとマッチャーが作成されます。したがって、このコードスニペット:

for (String s : myStringList) {
    if ( s.matches("\\d+") ) {
        doSomething();
    }
}

...これとまったく同じです:

for (String s : myStringList) {
    if ( Pattern.compile("\\d+").matcher(s).matches() ) {
        doSomething();
    }
}

明らかに、それは多くの不必要な作業を行っています。実際、正規表現をコンパイルしてパターンオブジェクトをインスタンス化するのは、実際に一致させるよりも簡単に時間がかかります。したがって、通常、そのステップをループから外すことは理にかなっています。事前にMatcherを作成することもできますが、それほど高価ではありません。

Pattern p = Pattern.compile("\\d+");
Matcher m = p.matcher("");
for (String s : myStringList) {
    if ( m.reset(s).matches() ) {
        doSomething();
    }
}

.NET正規表現に慣れている場合は、Javaのcompile()メソッドが.NETのRegexOptions.Compiled修飾子に関連しているかどうか疑問に思うかもしれません。答えはいいえだ。JavaのPattern.compile()メソッドは、.NETのRegexコンストラクターと同等です。Compiledオプションを指定する場合:

Regex r = new Regex(@"\d+", RegexOptions.Compiled); 

...正規表現をCILバイトコードに直接コンパイルして、はるかに高速に実行できるようにしますが、事前処理とメモリ使用にかなりのコストがかかります-正規表現のステロイドと考えてください。Javaには同等のものはありません。裏で作成されたパターンと、でString#matches(String)明示的に作成したパターンに違いはありませんPattern#compile(String)

(編集:私はもともとすべての.NETの正規表現オブジェクトが間違っていた、キャッシュされていることを述べ、.NET 2.0ので、自動キャッシュのような静的メソッドでのみ発生Regex.Matches()。ないあなたが直接正規表現のコンストラクタを呼び出し、REF


1
しかし、これは、PatternクラスでのそのようなTRIVIALメソッドの重要性を説明していません。私は常に、静的メソッドPattern.compileは、新しいPattern(regex、0)への単純なSHORTCUTをはるかに超えるものであると常に想定していました。コンパイルされたパターンのキャッシュを期待していました...私は間違っていました。たぶん、キャッシュを作成する方が、新しいパターンを作成するよりもコストがかかりますか??!
marcolopes 14

9
Matcherクラスはスレッドセーフではないため、スレッド間で共有しないでください。一方、Pattern.compile()はそうです。
gswierczynski 2014

1
TLDR; 「... [Pattern.compile(...)]は、正規表現を直接CILバイトコードにコンパイルします。これにより、パフォーマンスを大幅に向上させることができますが、
事前

3
MatcherはPattern.compileほど高価ではないことは事実ですが、数千の正規表現一致が発生し、事前にMatcherを作成してMatcher経由で再利用することにより、さらに大幅な節約が行われたシナリオで、いくつかのメトリックを実行しました.reset()。何千回も呼び出されるメソッドでヒープ内に新しいオブジェクトが作成されないようにすることで、通常はCPU、メモリ、そしてGCの負荷が大幅に軽減されます。
Volksman

Matcherオブジェクトはスレッドセーフではないため、安全ではない一般的なアドバイスである@Volksman。また、質問には関係ありません。しかし、はい、reset割り当てを減らすために、一度に1つのスレッドでのみ使用されるマッチャーオブジェクトを使用できます。
AndrewF

40

コンパイル正規表現を解析しメモリ内表現を構築します。コンパイルするオーバーヘッドは、一致と比較して重要です。パターンを繰り返し使用している場合は、コンパイルされたパターンをキャッシュするパフォーマンスが向上します。


7
さらに、追加のフラグパラメータを渡して、コンパイル中にcase_insensitive、dot_allなどのフラグを指定できます
Sam Barnum

17

コンパイルすると、PatternJavaは計算を実行して、Strings での一致の検索を高速化します。(正規表現のメモリ内表現を構築します)

Pattern複数回再利用する場合は、Pattern毎回新しいものを作成するよりもパフォーマンスが大幅に向上します。

パターンを1回だけ使用する場合、コンパイル手順は余分なコード行のように見えますが、実際には、一般的なケースでは非常に役立ちます。


5
もちろん、すべてを1行で記述できますMatcher matched = Pattern.compile(regex).matcher(text);。これには、単一のメソッドを導入するよりも利点があります。引数には効果的な名前が付けられ、Patternパフォーマンスを向上させるために(またはメソッド間で分割するために)を除外する方法は明らかです。
トム・ホーティン-タックライン2009年

1
Javaについてはよくご存じのようです。彼らは彼らのために働くためにあなたを雇うべきです...
jjnguy 2009年

5

パフォーマンスとメモリ使用量の問題であり、コンパイルされたパターンをコンパイルして、それを頻繁に使用する必要がある場合は保持します。正規表現の一般的な使用法は、検証済みのユーザー入力(フォーマット)とユーザーの出力データのフォーマットです。。これらのクラスでは、コンパイルされたパターンを保存しますが、通常はよく呼び出されます。

以下は実際にたくさん呼ばれるサンプルバリデーターです:)

public class AmountValidator {
    //Accept 123 - 123,456 - 123,345.34
    private static final String AMOUNT_REGEX="\\d{1,3}(,\\d{3})*(\\.\\d{1,4})?|\\.\\d{1,4}";
    //Compile and save the pattern  
    private static final Pattern AMOUNT_PATTERN = Pattern.compile(AMOUNT_REGEX);


    public boolean validate(String amount){

         if (!AMOUNT_PATTERN.matcher(amount).matches()) {
            return false;
         }    
        return true;
    }    
}

@Alan Mooreが述べたように、コードに再利用可能な正規表現がある場合(ループの前など)、パターンをコンパイルして保存し、再利用する必要があります。


2

Pattern.compile()正規表現を複数回再利用できるようにします(スレッドセーフです)。パフォーマンスの向上は非常に重要です。

私は簡単なベンチマークを行いました:

    @Test
    public void recompile() {
        var before = Instant.now();
        for (int i = 0; i < 1_000_000; i++) {
            Pattern.compile("ab").matcher("abcde").matches();
        }
        System.out.println("recompile " + Duration.between(before, Instant.now()));
    }

    @Test
    public void compileOnce() {
        var pattern = Pattern.compile("ab");
        var before = Instant.now();
        for (int i = 0; i < 1_000_000; i++) {
            pattern.matcher("abcde").matches();
        }
        System.out.println("compile once " + Duration.between(before, Instant.now()));
    }

compileOnceは3倍から4倍高速でした。私はそれが正規表現自体に大きく依存していると思いますが、頻繁に使用される正規表現の場合、私はstatic Pattern pattern = Pattern.compile(...)


0

正規表現をプリコンパイルすると、速度が向上します。マッチャーを再利用すると、わずかに速度が向上します。メソッドが頻繁に呼び出される場合、ループ内で呼び出されると、全体的なパフォーマンスが確実に上がります。


0

「Pattern.compile」と同様に、「RECompiler.compile」があります(com.sun.org.apache.regexp.internalから)。ここで、
1.パターン[az]のコンパイル済みコードには「az」が含まれています
2.コンパイル済みコードパターン[0-9]には '09'が含まれています
ます3.パターン[abc]のコンパイル済みコードには 'aabbcc'が含まれています。

したがって、コンパイルされたコードは、複数のケースを一般化する優れた方法です。したがって、異なるコード処理状況の代わりに1,2および3。問題は、コンパイルされたコードの現在の要素と次の要素のASCIIとの比較、つまりペアと比較して減少します。したがって
、aとzの間にasciiがあるものはすべて、aとzの間にあり
ます。「a」と「a」の間にアスキーがあるものはすべて「a」です


0

パターンクラスは正規表現エンジンのエントリポイントです。Pattern.matches()およびPattern.comiple()を介して使用できます。#これら2つの違い。 matches() -テキスト(String)が特定の正規表現comiple()と一致するかどうかをすばやく確認する -パターンの参照を作成します。したがって、複数回使用して、正規表現を複数のテキストと照合できます。

参考のため:

public static void main(String[] args) {
     //single time uses
     String text="The Moon is far away from the Earth";
     String pattern = ".*is.*";
     boolean matches=Pattern.matches(pattern,text);
     System.out.println("Matches::"+matches);

    //multiple time uses
     Pattern p= Pattern.compile("ab");
     Matcher  m=p.matcher("abaaaba");
     while(m.find()) {
         System.out.println(m.start()+ " ");
     }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.