2012-03-26 37 views
5

我正在尋找C#中的快速方法來查找字符串中的所有日期(該字符串是一個大文本,我要掃描大約200,000個不同的字符串)。由於有很多寫日期的方法(例如2012年12月31日或2012年12月31日以及更多), 我使用這個正則表達式(它應該涵蓋幾乎所有常用的寫日期方式):在字符串中找到日期

串findDates =「: - |(((\ d {1,4})/.- /?):(\ S \ d {1,2})\ S +(一月( ?:uary){0,1} \ {0,1} | 2月(:?ruary){0,1} \ {0,1} | 3月(:CH){0,1} \ {? 0,1} |四月(?:IL){0,1} \ {0,1} | \可能{0,1} |六月:{0,1} \ {0,1(ΔE)。 } |七月(?:Y){0,1} \ {0,1} |八月(:UST?){0,1} \ {0,1} |九月(?:tember){0,1 } \ {0,1} |倍頻程(?:奧伯){0,1} \ {0,1} |十一月(?:燼){0,1} \ {0,1} | DEC(? :餘燼){0,1} \ {0,1})\ S +(\ d {2,4}))|(:(一月(:?。uary){0,1} \ {0,1 } | 2月(?:ruary){0,1} \ {0,1} | 3月:{0,1} \ {0,1} |四月(CH 3)。(?:IL){0,1 } \ {0,1} |。可以\ {0,1} |。君(?: E){0,1} \ {0,1} |七月(:?y)的{0,1} \ {0,1} |八月(:UST){0,1} \ {0,π。 1} | SEP(:tember){0,1} \ {0,1} |十月(?:奧伯){0,1} \ {0,1} |十一月(?:餘燼){0, 1} \ {0,1} |分解(?:餘燼)。{0,1} \ {0,1})\ S +([0-9] {1,2})[\ S,] +( \ d {2,4}))「;

「RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace」標記。 也,我試圖預編譯的正則表達式,使其更快。

問題是它很慢(在一些文本超過2秒) 有沒有更好和有效的方法來做到這一點?

謝謝

+0

一個簡單的評論,但我會嘗試在那裏正則表達式一個接一個。首先用第一個正則表達式掃描並刪除匹配的單詞,然後運行另一個。根據輸入的字符串,它可能會更快。 – daryal 2012-03-26 11:47:27

+1

'{0,1}'與'?'相同。改變不會加快,但簡化閱讀一下。 – kirilloid 2012-03-26 11:53:45

+0

如果您使用'RegexOptions.ExplicitCapture',它會更快一些,您不必使用這些'(?:)'組。 – 2012-03-26 12:05:33

回答

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})- /.- /.)在格式化中丟失了某些東西,或者這對於一點都沒有幫助。

\ d {1,4}對一年或任何其他日期部分有意義,但- /.- /.完全沒有意義。我認爲你的意思是這樣的:

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

或在這方面的東西。就其本身而言,它捕獲垃圾,可能不會加速匹配過程。

最後,我同意Aliostad的觀點,您可能更適合尋找不太精確的模式來查找初始候選項,然後使用DateTime.TryParseExact或使用額外的一組表達式來縮小結果。

與其創建「全局」表達式來查找候選人,您可以使用大量精確表達式。你會發現使用正則表達式,在大量輸入中運行大量精確表達式比運行一個表達式中包含許多|和s的表達式更經濟。

所以,你的搜索分解成多個非常精確表達式可能會導致很多更高的性能,這可能是一個開始:

\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'以及'sep'以及'september'

分解模式也提高了可讀性:)。

最後一個提示:通過限制\ s +之類的東西來限制可能需要回溯的字符數量,但很少需要20,000個空格匹配,但是如果它們位於源文檔中,它將嘗試以匹配他們。 \ {{1,20}}通常已經足夠,並且限制了引擎在沒有真正不匹配的情況下嘗試獲得比賽的能力。

+0

這真的很有幫助評論,謝謝。 – meirlo 2012-03-26 14:56:52

+0

+1良好的分析。 – Aliostad 2012-03-26 15:29:22

3

很難拿出一個算法沒有測試它。我們可以推薦一些結果更慢的東西。所以真的是嘗試不同的選擇。

你的表情看起來有點冗長,但我不能說它是問題的原因。 2秒一個大文件是確定的,但不是一個較小的文件,因此它是所有相對於工件的尺寸是做


一種方法,我可以推薦是有一個兩個階段的過程。

第一個是篩選釣魚最有可能匹配的和另一個是進一步檢查文件的比賽所在的部分。例如,'\ d {1,2} \ s *,\ s * \ d {4}'可能是日期的一部分,但是尋找它比查找Jan(uary)/ Feb (ruary)/月(CH)/ ....


和一個小忠告:首先得到的指標權,開始做任何更改之前建立的基礎指標的功課。

如果你想提高性能,你甚至在嘗試改進之前必須有一些硬性指標。