6
var email = '[John Smith] <johnsmith@gmail.com>';

var re1 = /.*<+(.*)+>.*/;
var re2 = /.*\[+(.*)+\].*/;

var address = email.replace(re1, "$1");
var name = email.replace(re2, "$1");

2番目の正規表現(名前を取得するため)の実行が非常に遅いことがわかりました。しかし、最初のものは問題ありません。これはなぜですか?必要な文字列を取得するためのより良い方法はありますか?

4

3 に答える 3

16

正規表現が遅い理由は、それらがひどく書かれているからです。

では、なぜ彼らが悪いのかを述べていきましょう。

最初の式には不要なトークンがたくさんあります。先頭と末尾など、.*違いはありません。次に、<0 から inf までの時間を定量化しました。なんで?あなたは一致したい<<<<<<<<email>ですか?またはemail>?最後に、繰り返しグループを数量化しました。これは恐ろしいから

  1. 定量化されたキャプチャ グループはそれ自体を上書きします
  2. 上記のステートメントにより、キャプチャ グループを使用しても意味がなく、不要なリソースが使用されます。

さて、それが最初の表現です。に切り替えたばかりなのに、2番目のものはさらに悪い<>です[]。なぜあなたは尋ねるかもしれませんか?その理由をお話しします。一致しないため。なぜこれがそんなに悪いのですか?それは私たちが壊滅的なバックトラッキングと呼ぶものを生み出すからです。なぜこれを行うのでしょうか? その理由を説明します。

.*可能な限り一致するようにします。実際、最初は文字列全体を消費します。明らかにそれは失敗するため、最初の に一致するまで何度もバックトラックします[。これで、エンジンはリテラルの文字列の最初の位置で一致を見つけました[(したがって、.*一致は何もありません)。次のトークンは、.*貪欲な性質により、再びすべてに一致します。これは機能しないため、エンジンはバックトラックします。文字列に一致するまで、これを試行し続けます。問題は、決してそうならないことです。貪欲な量指定子が、1 つ以上の一致を必要とする量化されたグループに囲まれているためです。

さて、これをどのように修正しますか?+グループの後ろから を簡単に削除できます。それはそれを修正します。正規表現は依然としてひどいものですが、エンジンが何百万回もバックトラックすることはありません。どうすればそれをさらに改善できますか?否定文字クラスを使用する。

/\[([^]]+)\] <([^>]+)>/

ここで正規表現のデモを表示: http://regex101.com/r/wS2jN0

最初に regex101.com を使用していた場合、バックトラッキングの問題にすぐに気付くでしょう: http://regex101.com/r/vB8xB0

于 2013-01-07T00:14:28.740 に答える
4

パフォーマンスの問題 (ある場合) についてはわかりませんが、単一の正規表現を使用して両方の値を抽出できます。

var str = '[John Smith] <johnsmith@gmail.com>',
    re = /\[(.+)\] <(.+)>/,
    name = str.match( re )[1],
    email = str.match( re )[2];

console.log( name, email ); //=> "John Smith johnsmith@gmail.com"
于 2013-01-06T23:59:34.173 に答える
3

これは.*、文字列がの形式を持っているという事実と組み合わされた多くの貪欲なものの使用によるもの"[..] <..>"です。

.*を使用せずに使用するたび?に、RegExpエンジンは文字列全体の残りを選択し、RegExpの次の部分が失敗すると、一度に1文字ずつ後方に移動し、次の部分をテストします。

sを繰り返したので.*、これは、RegExpエンジンがバックトラックする必要がある文字列の末尾から、各文字に対して指数関数的に多くのテストを実行するように指示していることを意味します。+その後、貪欲な兆候によってさらに悪化し、何をしているのかを繰り返し*ます。

?文字列について詳しく知っていて、文字列内でそれほど多くを探していないため、ここでsを追加するだけでは最善の修正ではありません。したがって、 「悪い」状態を少なくするために、関心のあるビットのみを一致させるなどの操作を行います。

var re1 = /\<([^>]*)>/,
    re2 = /\[([^\]]*)\]/;

var address = email.match(re1)[1],
    uname = email.match(re2)[1]; // to avoid `window.name` conflict
于 2013-01-07T00:25:56.770 に答える