在字符串中查找日期

mei*_*rlo 5 c# regex performance

我正在寻找C#中的快速方法来查找字符串中的所有日期(字符串是一个大文本,我要扫描大约200,000个不同的字符串).

由于有很多方法可以写日期(例如2012年12月31日或2012年12月31日等等),我正在使用这个正则表达式(应该涵盖几乎所有常用的写日期方式):

string findDates ="(?:(\d {1,4}) - /.- /.)|(?:(\\\\\\\\\\\\\\\\\ 1}\{0,1} | 2月.(?:ruary){0,1}\{0,1} |三月(:CH 2){0,1}\{0,1} |的apr(. ?:IL){0,1}\{0,1} | \可能{0,1} |君(:.E){0,1}\{0,1} |七月(?:?ÿ ){0,1}\{0,1} |八月(:UST){0,1}\{0,1} |九月(?:?tember){0,1}\{0,1 } |十月(?:奥伯){0,1}\{0,1} |十一月(?:烬){0,1}\{0,1} | 12月(?:烬){0,1 }\{0,1})\ S +(\ d {2,4}))|(:(一月(: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} |分解(?:余烬).{0,1}\{0,1})\ S +([O- 9] {1,2})[\ S,] +(\ d {2,4}))";

使用"RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace"标记.此外,我试图预编译正则表达式,使其更快.

问题是它非常慢(在一些文本上超过2秒)是否有更好和有效的方法来做到这一点?

谢谢

jes*_*ing 3

该表达式总体上看起来不错,正如其他人提到的那样,它可能有点冗长,所有的{0,1}other of?和 the (?:other of applications RegexOptions.ExplicitCapture。但这些不应该让表达变慢。它们只会带来更好的可读性。

可能导致速度缓慢的原因是,通过将扩展的月份和 . 选修的。我想知道如果您将表达式更改为仅应用可选 会发生什么。一次,在月份名称之后,如果将月份名称设为贪婪组((?>pattern)非回溯(或“贪婪”)子表达式)会发生什么。

以便:

 (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}))
Run Code Online (Sandbox Code Playgroud)

会成为:

 (?>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}))
Run Code Online (Sandbox Code Playgroud)

它不仅更短,而且我希望它更快。

然后是开头的表达式部分,这对我来说并没有真正意义。(?:(\d{1,4})- /.- /.)要么在格式中丢失了某些内容,要么这没有一点帮助。

\d{1,4} 对于一年或任何其他日期部分有意义,但其后- /.- /.根本没有意义。我想你的意思是这样的:

 \d{1,4}[- /.]\d{1,2}[- /.]\d{1,2}
Run Code Online (Sandbox Code Playgroud)

或者那个地区的东西。就目前情况而言,它会捕获垃圾,可能不会加快匹配过程。

最后,我同意 Aliostad 的观点,您可能最好尝试找到不太精确的模式来查找初始候选者,然后使用 DateTime.TryParseExact 或使用一组附加表达式来缩小结果范围。

您可以使用大量精确表达式,而不是创建“全局”表达式来查找候选者。您会发现,使用 Regex,在大输入上运行多个精确表达式通常比运行一个包含大量 | 和 ? 的表达式要便宜。

因此,将搜索分解为多个非常精确的表达式可能会带来更高的性能,这些可能是一个开始:

 \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
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,所有可选组都已从这些表达式中删除,从而使它们的运行速度更快。我还删除了月份名称中的确切拼写,因为您可能想要接受“sept”、“sep”和“september”

打破模式还可以提高可读性:)。

最后一个提示:限制您需要回溯的可能字符数量,通过对 \s+ 等内容进行限制,您很少需要 20,000 个空格来匹配,但如果它们在您的源文档中,它会尝试匹配它们。\s{1,20} 通常就足够了,并且限制了引擎尝试获得实际上不存在的匹配的能力。