2

次の正規表現に問題があります。

(.*?)\|\*\|([0-9]+)\*\|\*(.*?)

次のようなものと一致する必要があります。|*25 *|

.Net Framework 4RegExクラスを使用しています。コードは次のとおりです。

string expression = "(.*?)" + 
       Regex.Escape(Constants.FIELD_START_DELIMITER_BACK_END) + 
       "([0-9]+)" + 
       Regex.Escape(Constants.FIELD_END_DELIMITER_BACK_END) + 
       "(.*?)";
Regex r = new Regex(expression);
r.Matches(contentText)

40.000文字のテキストでは時間がかかりすぎます(60秒など)。

しかし、180.000のテキストでは、その速度は非常に許容範囲内です(3秒以下)

テキスト間の唯一の違いは、最初のテキスト(遅いテキスト)がすべて1行に含まれ、改行がないことです。これは問題になる可能性がありますか?それはパフォーマンスに影響を与えていますか?

ありがとう

4

2 に答える 2

5

@David Gorslineの解決策(コメントから)は正しいです:

string expression =
    Regex.Escape(Constants.FIELD_START_DELIMITER_BACK_END) + 
    "([0-9]+)" + 
    Regex.Escape(Constants.FIELD_END_DELIMITER_BACK_END);

具体的に(.*?)は、最初にあなたをやっています。それは、正規表現エンジンがそれ自体で行うべきことを引き継ぐことです-正規表現が一致する可能性のある次の場所をスキャンします-そしてそれをはるかに効率的に行いません。各位置で(.*?)、正規表現の次の部分が一致するかどうかを判断するために効果的に先読みを実行し、それが失敗した場合にのみ、先に進んで次の文字を消費します。

ただし、のようにもっと効率的なものを使用したとしても、[^|]*速度は低下します。ただし、その部分はオフのままにしておくと、正規表現エンジンは代わりに、おそらくボイヤームーア文字やクヌースモリス文字などのアルゴリズムを使用して、正規表現の最初の定数部分をスキャンできます。したがって、一致させたいビットの周囲について心配する必要はありません。探しているものを正規表現エンジンに伝えて、邪魔にならないようにしてください。

一方、トレーリング (.*?)は実際には何もしないため、実質的に効果はありません。?ターンは気が進まないので、.*それを先に進めて次のキャラクターを消費させるには何が必要ですか?正規表現でそれを強制する何かがそれに続く場合にのみそうします。たとえばfoo.*?bar、次の「foo」から次の「bar」まですべてを消費しますが、「foo」をfoo.*?消費するとすぐに停止します。正規表現の最後に気が進まない数量詞を使用することは、決して意味がありません。

于 2012-04-13T23:45:48.263 に答える
2

あなたはあなたの質問に答えました:問題は、.新しい行と一致しないことです(デフォルトでは一致しません)。これにより、多くの試行が失敗します。40000文字列のすべての位置にほぼ1回です。
長いが1行のファイルでは、エンジンはファイルの1回のパスでパターンを一致させることができます(一致が成功した場合、そうでない場合は、失敗するまでに長い時間がかかると思われます...)。
行数が多い短いファイルでは、エンジンは最初の文字から一致しようとします。最初の行の終わりまで一致.*?し(これは怠惰な一致であるため、さらに多くのことが発生していますが、それを無視します)、失敗します。今、それは2番目のキャラクターから再び統計します、2行目ではありません!これにより、数値を一致させる前でも、n²の複雑さが生じます。

簡単な解決策は、.改行を一致させることです。

Regex r = new Regex(expression, RegexOptions.Singleline);

絶対開始アンカーと終了アンカーを使用して、最初から最後まで一致することを確認することもでき\Aます\z

string expression = "\\A(.*?)" + 
   Regex.Escape(Constants.FIELD_START_DELIMITER_BACK_END) + 
   "([0-9]+)" + 
   Regex.Escape(Constants.FIELD_END_DELIMITER_BACK_END) + 
   "(.*?)\\z";

別の注意:Davidがコメントで示唆しているように、\|\*\|([0-9]+)\*\|\*十分に機能するはずです。試合の前後にすべてのテキストを「キャプチャ」する必要がある場合でも、試合の位置を使用して簡単にテキストを取得できます。

于 2012-04-13T19:35:48.183 に答える