9

Javaで、正規表現を使用してパターンマッチングを実行しようとすると。たとえば、入力文字列を取得し、正規表現を使用して数値かどうかを確認します。そうでない場合は、例外をスローします。この場合、正規表現を使用すると、文字列の各文字を取得する場合よりもコードの冗長性が低くなり、それが数値であるかどうかを確認し、そうでない場合は例外をスローすることを理解しています。

しかし、私は正規表現によってプロセスがより効率的になると想定していました。これは本当ですか?この点についての証拠は見つかりません。正規表現は舞台裏でどのように試合を行っていますか?文字列を繰り返し処理して、各文字を1つずつチェックしていませんか?

4

8 に答える 8

4

楽しみのために、私はこのマイクロベンチマークを実行しました。最後の実行(つまり、JVMウォームアップ後/ JIT後)の結果は以下のとおりです(結果は、いずれにせよ、ある実行から別の実行までかなり一貫しています):

regex with numbers 123
chars with numbers 33
parseInt with numbers 33
regex with words 123
chars with words 34
parseInt with words 733

言い換えると、charsは非常に効率的であり、文字列が数値の場合はInteger.parseIntはcharと同じくらい効率的ですが、文字列が数値でない場合は非常に遅くなります。正規表現はその中間です。

結論

文字列を数値に解析し、文字列が一般に数値であると予想される場合は、Integer.parseIntを使用するのが最善の解決策です(効率的で読みやすい)。文字列が数字でない場合に得られるペナルティは、頻度が高すぎない場合は低くする必要があります。

ps:私の正規表現はおそらく最適ではありません。コメントしてください。

public class TestNumber {

    private final static List<String> numbers = new ArrayList<>();
    private final static List<String> words = new ArrayList<>();

    public static void main(String args[]) {
        long start, end;
        Random random = new Random();

        for (int i = 0; i < 1000000; i++) {
            numbers.add(String.valueOf(i));
            words.add(String.valueOf(i) + "x");
        }

        for (int i = 0; i < 5; i++) {
            start = System.nanoTime();
            regex(numbers);
            System.out.println("regex with numbers " + (System.nanoTime() - start) / 1000000);
            start = System.nanoTime();
            chars(numbers);
            System.out.println("chars with numbers " + (System.nanoTime() - start) / 1000000);
            start = System.nanoTime();
            exception(numbers);
            System.out.println("exceptions with numbers " + (System.nanoTime() - start) / 1000000);

            start = System.nanoTime();
            regex(words);
            System.out.println("regex with words " + (System.nanoTime() - start) / 1000000);
            start = System.nanoTime();
            chars(words);
            System.out.println("chars with words " + (System.nanoTime() - start) / 1000000);
            start = System.nanoTime();
            exception(words);
            System.out.println("exceptions with words " + (System.nanoTime() - start) / 1000000);
        }
    }

    private static int regex(List<String> list) {
        int sum = 0;
        Pattern p = Pattern.compile("[0-9]+");
        for (String s : list) {
            sum += (p.matcher(s).matches() ? 1 : 0);
        }
        return sum;
    }

    private static int chars(List<String> list) {
        int sum = 0;

        for (String s : list) {
            boolean isNumber = true;
            for (char c : s.toCharArray()) {
                if (c < '0' || c > '9') {
                    isNumber = false;
                    break;
                }
            }
            if (isNumber) {
                sum++;
            }
        }
        return sum;
    }

    private static int exception(List<String> list) {
        int sum = 0;

        for (String s : list) {
            try {
                Integer.parseInt(s);
                sum++;
            } catch (NumberFormatException e) {
            }
        }
        return sum;
    }
}
于 2012-08-09T01:25:04.457 に答える
3

まだ技術的な答えはありませんが、コードを書いて見ることができます。正規表現が文字列を数値に変換する方法になるとは思いません。多くの場合、それらはより効率的ですが、それがうまく書かれていない場合、それは遅くなります。

しかし、なぜあなたはただ使っていないのです Integer.parseInt("124")か?これにより、NumberFormatExceptionがスローされます。それを処理できるはずであり、それはコアJavaに任せて数の検出を任せます。

于 2012-08-09T01:18:57.960 に答える
1

次のようなものよりも、どのように簡単に、または読みやすくなるのかわかりません。

Integer.parseInt()

また

Double.parseDouble()

それらは、無効な入力に対して例外をスローすることを含め、あなたが説明したことを正確に実行します。

パフォーマンスについて:正規表現は上記よりも効率が悪いと思います。

于 2012-08-09T01:45:18.343 に答える
1

舞台裏の正規表現について...

有限状態マシン(FSM)は、正規表現に相当します。FSMは、言語(あなたの場合は番号)を認識できるマシンです。FSMには、アルファベット、状態、初期状態、N-最終状態、およびある状態から別の状態への遷移関数があります。文字列はアルファベットに含まれている必要があります(たとえばASCII)。FSMは初期状態から始まります。文字列を入力すると、function(state、char)=>stateに応じて状態から状態に移動するcharごとにcharが処理されます。最終状態に達すると、文字列が数値であるかどうかがわかります。

詳細については、FSMおよびAutomata-based_programmingを参照してください。

于 2012-08-09T01:43:41.837 に答える
1

ちょうど私の5セント:)一般に、正規表現言語は整数または文字列のみを解析することを目的としていません。これは、任意の「正規表現」を認識できる非常に強力なツールです。それは私の大学時代を思い出させます(オートマトン理論コースを覚えていますか?:)、しかしここに正規言語が実際に何であるかを説明するリンクがあります

現在、FSMを構築しているため、オーバーヘッドが発生します。したがって、Integer.parseInt正規表現エンジンは適切な代替ではない可能性があります。さらに、Javaはより具体的なAPIを導入しました。ただし、正規表現は、より複雑な式を処理する場合や、多くの式がある場合に利点があります。

正規表現は賢明に使用する必要があります。パターンは常にコンパイルする必要があります(そうしないと、毎回パターンをコンパイルするとパフォーマンスが低下するため、効率的に再利用できません)

より複雑な入力でテストを実行し、何が起こるかを確認することをお勧めします。

于 2012-08-09T05:04:24.157 に答える
0

確かに言うのは難しいですが、一般的に、正規表現は明示的な文字チェックに比べて効率が悪い可能性があります。REは最終状態のオートマトンであるため、オートマトンの構築と保守にはいくらかのオーバーヘッドがあります。私の実践では、明示的なコードは正規表現よりも常に高速です(したがってより効率的です)。

しかし、ここにジレンマがあります。正規表現は、ほとんどの場合、配信までの時間の観点からより効率的であり、正しく使用すると読みやすくなります。そして、ここに別のジレンマがあります。正規表現の正しい使用法はめったに見られません...

あなたのシナリオでは、グアバライブラリを使用することをお勧めします。

boolean isValid = DIGIT.matchesAllOf("1234");
于 2012-08-09T01:20:41.587 に答える
0

最終的には、実際に文字列を反復処理し、提供されたパターンに一致するものを見つけようとして各文字をチェックします。さらに、バックトラッキングを使用します(一致する可能性のある方法が多数ある場合、エンジンはそれらすべてを試行します)。これにより、一部の異常なケースではパフォーマンスが非常に低下する可能性があります(これに遭遇する可能性は低いですが、理論的には可能です)。最悪の場合、Java正規表現エンジンのパフォーマンスはO(2 N)です。ここで、Nは入力文字列の長さです。

O(N)パフォーマンスを提供するが、Java正規表現と比較して機能が少ない、はるかに高速なパターンマッチングのためのアルゴリズムがあります。

これは、この質問について詳しく説明している記事です。

ただし、ほとんどの場合、正規表現エンジンはアプリケーションのパフォーマンスのボトルネックにはなりません。それは十分に速いので、プロファイラーがそれを指さない限り、一般的にそれについて心配する必要はありません。また、アルゴリズムの宣言型の説明を提供します。これは、ほとんどの場合、反復アルゴリズムの実装がはるかに冗長で読みにくくなるため、非常に便利です。

于 2012-08-09T01:24:24.943 に答える
0

あなたの質問に具体的に答えるには:

複雑なテキストに正規表現パターンマッチングを適用してから、同じマッチングコードを自分で作成してみてはどうでしょうか。

どちらが速いかを確認してください。

回答:正規表現。

于 2012-08-09T01:39:12.667 に答える