2012-03-26 1 views
5

文字列内のすべての日付を見つけるためのC#の高速な方法を探しています(文字列は大文字で、約200,000種類の文字列をスキャンします)。日付を文字列で検索する

私はこの正規表現を使用しています(日付を書くためのほとんどすべての方法を網羅しています)、日付を書く方法がたくさんあるので(例:31/12/2012または2012年12月31日など)

列findDates =「 - |(((\ dの{1,4})/.- /?):(\ S \ dの{1,2})\ S +(1月( {0,1} | {0,1} | {0,1} | {0,1} | {0,1} {0,1} | apr(?:il){0,1} \。{0,1} | may \。{0,1} | jun(?:e){0,1} \。{0,1 {0,1} | {0,1} | aug(?:ust){0,1} \。{0,1} | sep(?:tember){0,1} {0,1} | {0,1} | {0,1} | {0,1} | {0,1} | {0,1} {0,1} \。{0,1})\ s +(\ d {2,4}))|(?(jan(?:uary){0,1} \。{0,1 {0,1} | apr(?:il){0,1} {0,1} | {0,1} } \。{0,1} | may \。{0,1} | jun(?:) {0,1} |。{0,1} \。{0,1} | jul(?:y){0,1} \。{0,1} | aug(?:ust){0,1} \。{0、 {0,1} | {0,1} | {0,1} | sep(?:tember){0,1} \ {0,1} {\ s、] +({0,1}){ \ d {2,4})) ";

"RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace"タグを使用します。 また、正規表現をあらかじめコンパイルして、それをさらに高速化しようとしました。

問題は非常に遅いです(一部のテキストでは2秒以上) これを行うには効率的な方法がありますか?

ありがとう

+0

簡単なコメントですが、私は正規表現を1つずつ試してみます。まず、最初の正規表現でスキャンし、一致する単語を削除してから、もう一方の正規表現を実行します。入力文字列に応じてより速くなる場合があります。 – daryal

+1

'{0,1}'は '?'と同じです。変更はスピードアップされませんが、少し読んだだけで簡単に変更できます。 – kirilloid

+0

'RegexOptions.ExplicitCapture'を使うと少し速くなり、'(?:) 'グループを使う必要はありません。 –

答えて

3

式は、他の人が言及したように、それは代わりに?のすべて{0,1}、代わりにRegexOptions.ExplicitCaptureを適用する(?:と少し冗長かもしれないが、全体的によさそうです。しかし、これらは表現を遅くするべきではありません。それらは読みやすさを向上させます。

遅くなる原因は、拡張された月と拡張月の両方を作成することで、式にバックトラックオプションがたくさんあることです。オプション。オプションを適用するだけで式を変更した場合、どうなるでしょうか。一度、月の名前、そして何を貪欲グループ月の名前を作ったらどうなる

後だから((?>pattern) Nonbacktracking(または「貪欲」)部分式。):

(jan(?:uary){0,1}\.{0,1}|feb(?:ruary){0,1}\.{0,1}|mar(?:ch){0,1}\.{0,1}|apr(?:il){0,1}\.{0,1}|may\.{0,1}|jun(?:e){0,1}\.{0,1}|jul(?:y){0,1}\.{0,1}|aug(?:ust){0,1}\.{0,1}|sep(?:tember){0,1}\.{0,1}|oct(?:ober){0,1}\.{0,1}|nov(?:ember){0,1}\.{0,1}|dec(?:ember){0,1}\.{0,1})\s+(\d{2,4})) 

はなる:

(?>jan(uary)?|feb(ruary)?|mar(ch)?|apr(il)?|may|june?|july?|aug(ust)?|sep(tember)?|oct(ober)?|nov(ember)?|dec(ember)?)\.?\s+(\d{2,4})) 

これははるかに短いだけでなく、もっと速くなると期待しています。

最初に式の部分がありますが、これは実際には意味がありません(?:(\d{1,4})- /.- /.)書式設定で何かが失われているか、これが1つのビットに役立っていません。

\ d {1,4}は1年または他の日付部分には意味がありますが、- /.- /.は意味を成立させません。私はあなたのようなものを意味すると思う:

\d{1,4}[- /.]\d{1,2}[- /.]\d{1,2} 

またはその領域のもの。それはそれが立っているので、おそらくマッチングプロセスをスピードアップしないゴミをキャプチャします。

最後に私はAliostadに同意します。最初の候補を見つけるためにあまり正確ではないパターンを見つけようとする方がよいでしょう。そしてDateTime.TryParseExactか追加の式セットを使って結果を絞ります。

候補を見つけるために 'グローバル'式を作成する代わりに、正確な表現をたくさん使用することができます。 Regexでは、多くの||と?を使って1つの式を実行するよりも、多くの正確な式を大きな入力で実行する方が安いことが分かります。

だから、多くの、より高いパフォーマンスにつながる可能性がある複数の非常に正確な表現に検索を壊し、これらは開始することができます

\b\d{1,2}[- .\\/]\d{1,2}[- .\\/](\d{2}|\d{4})\b 
\b((jan|feb|mar|apr|jun|jul|aug|sep|oct|nov|dec)(.|[a-z]{0,10})|\d{1,2})[- .\\/,]\d{1,2}[- .\\/,](\d{2}|\d{4})\b 

あなたはすべてのオプションのグループは、これらの式から削除作っている見ることができるようにそれらは実行するためにはるかに高速です。あなたはおそらく 'sept'と 'september'だけでなく 'sept'も受け入れたいので、月名から正確なスペルを削除しました。

パターンを分割すると読みやすさも向上します:)。

最後の1つのヒント:バックトラックする可能性のある文字の量を制限します。\ s +などの制限を設けても、20,000個のスペースを一致させることはめったにありませんが、ソースドキュメントにあれば、それらに一致する。 \ s {1,20}は通常十分であり、エンジンが実際には存在しない場所でマッチを試みる能力を制限します。

+0

本当に有益なコメントでした。ありがとうございました。 – meirlo

+0

+1良い分析。 – Aliostad

3

アルゴリズムを試してみるのは難しいです。遅くなったものを推薦することができます。だから実際には別のオプションを試しています。

あなたの表現は少し冗長に見えますが、問題の原因とは言えません。すべて、それは私が2段階のプロセスを持っているお勧めすることができます


一つのアプローチを行っている仕事のサイズに相対的であるので、大きなファイルのための2秒はOKではなく、小さいファイルのためです。

最初はの魚を釣っている可能性が最も高く、もう1つはさらにマッチしているファイルのセクションのみを調べることです。たとえば、 '\ d {1,2} \ s *、\ s * \ d {4}'は日付の一部である可能性が高いですが、Jan(uary)/ Feb (ruary)/月(CH)/ ....


やアドバイスの小片:は最初のメトリックが権利を取得、任意の変更を開始する前に、あなたの基本指標を確立するの宿題を。

パフォーマンスを向上させたい場合は、向上させようとする前に、いくつかのハードと高速のメトリックが必要です。

関連する問題