【问题标题】:Regex MatchCollection obj hangs\"Function evuluation timed out" after Regex.MatchesRegex MatchCollection obj 在 Regex.Matches 后挂起\“函数评估超时”
【发布时间】:2011-05-15 13:24:44
【问题描述】:

我对 C# 和正则表达式也很陌生,但是我已经搜索了几个小时来找到解决这个问题的方法,所以希望这对你们来说很容易:)

我的应用程序使用正则表达式来匹配给定字符串中的电子邮件地址, 然后遍历比赛。:

String EmailPattern = "\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*";
MatchCollection mcemail = Regex.Matches(rawHTML, EmailPattern);

foreach (Match memail in mcemail)

工作正常,但是,当我从某个页面http://www.sp.se/sv/index/services/quality/sidor/default.aspx 下载字符串时,MatchCollection(mcemail) 对象“挂起”循环。使用断点并访问对象时,我在所有内容(.Count 等)上得到“函数评估超时”。

更新 我已经在同一个字符串上尝试了我的模式和其他电子邮件模式,每个人(正则表达式设计者、基于 python 的网页等)在尝试匹配这个特定字符串时都会失败/超时。

如何检测到 matchcollection obj 尚未“准备好”使用?

【问题讨论】:

  • 这里有个提示:如果你在字符串前面加上@,你就不需要双反斜杠:@"\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*"

标签: c# .net


【解决方案1】:

如果您可以发布导致问题的电子邮件(可能以某种方式匿名),这将为我们提供更多信息,但我认为问题出在这个小家伙:

([-.]\\w+)*\\.\\w+([-.]\\w+)*

为了理解这个问题,让我们把它分成几组:

([-.]\\w+)*
\\.\\w+
([-.]\\w+)*

\\.\\w+ 匹配的字符串是与[-.]\\w+ 匹配的字符串的子集。因此,如果您的部分输入看起来像foo.bar.baz.blah.yadda.com,那么您的正则表达式引擎无法知道哪个组应该匹配它。那有意义吗?所以第一个([-.]\\w+)*可以匹配.bar.baz.blah,然后\\.\\w+可以匹配.yadda,最后一个([-.]\\w+)*可以匹配.com...

...OR 第一个子句可以匹配.bar.baz,第二个子句可以匹配.blah,最后一个子句可以匹配.yadda.com。由于它不知道哪个是正确的,它会不断尝试不同的组合。它最终应该会停止,但这仍可能需要很长时间。这称为“灾难性回溯”。

由于您使用的是capturing groups 而不是非捕获组,这个问题变得更加复杂;即([-+.]\\w+) 而不是(?:[-+.]\\w+)。这会导致引擎尝试分离并保存括号内的任何匹配项以供以后参考。但正如我上面解释的,每个子字符串属于哪个组是不明确的。

您可以考虑将 @ 之后的所有内容替换为以下内容:

\\w[-\\w]*\\.[-.\\w]+

这可以使用一些改进使其更具体,但你明白了一般的想法。希望我解释得足够好;分组和反向引用有点难以描述。

编辑:

回顾你的模式,这里有一个更深层次的问题,仍然与我提到的回溯/歧义问题有关。子句\\w+([-.]\\w+)* 本身就是模棱两可的。把它分成几部分,我们有:

\\w+
([-.]\\w+)*

假设您有一个类似foobar 的字符串。 \\w+ 结束和 ([-.]\\w+)* 开始在哪里? ([-.]\\w+) 有多少次重复?以下任何一项都可以作为匹配项:

f(oobar)
foo(bar)
f(o)(oba)(r)
f(o)(o)(b)(a)(r)
foobar
etc...

正则表达式引擎不知道哪个是重要的,所以它会全部尝试。这与我在上面指出的问题相同,但这意味着您在模式中的多个位置都有它。

更糟糕的是,([-.]\\w+)*模棱两可,因为+\\w 之后。 blah 有多少个群?我数了 16 种可能的组合:(blah)(b)(lah)(bl)(ah)...

即使是相对较小的输入,不同可能组合的数量也会很大,因此您的引擎将处于超速状态。如果我是你,我肯定会简化它。

【讨论】:

  • 谢谢,我也会尝试改变它。关于导致问题的电子邮件,我看不到。 MatchCollection 对象成员不是“可访问的”。我尝试了不同的模式(见我的帖子中的更新),一切似乎都挂了......“灾难性回溯”:D
  • @Johan Helsén - 查看该页面的源代码,我看到六个电子邮件地址。我怀疑如果您尝试其中包含六个电子邮件地址的 any 页面,由于您的正则表达式的资源密集度,该程序将挂起。顺便说一下,看看我的编辑。
  • 感谢您的解释。正如您通过查看我拥有的正则表达式所注意到的那样,我有很多东西要学习。
【解决方案2】:

我刚刚进行了本地测试,它似乎是纯粹的文档大小或ViewState 中的某些内容导致正则表达式匹配评估超时。 (编辑:实际上,我很确定它的大小。删除 ViewState 只会显着减小大小。)

一个公认的粗略的解决方法是这样的:

string[] rawHtmlLines = File.ReadAllLines(@"C:\default.aspx");
string filteredHtml = String.Join(Environment.NewLine, 
    rawHtmlLines.Where(line => !line.Contains("_VIEWSTATE")).ToArray());
string emailPattern = @"\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*";
var emailMatches = Regex.Matches(filteredHtml, emailPattern);

foreach (Match match in emailMatches)
{
    //...
}

总的来说,我怀疑电子邮件模式没有很好地优化(或打算)过滤掉大字符串中的电子邮件,而只是用作用户输入的验证。通常,将您搜索的字符串限制为您真正感兴趣的部分并使其尽可能小可能是个好主意 - 例如,通过省略保证不包含任何可读电子邮件地址的ViewState .

如果性能很重要,那么使用StringBuilderIndexOf(等)创建过滤后的 HTML 可能也是一个更好的主意,而不是拆分行并 LINQing 结果:)

编辑:

为了进一步最小化 Regex 需要检查的字符串的长度,您只能包含包含 @ 字符开头的行,如下所示:

string filteredHtml = String.Join(Environment.NewLine, 
    rawHtmlLines.Where(line => line.IndexOf('@') >= 0 && !line.Contains("_VIEWSTATE")).ToArray());

【讨论】:

  • 干杯,性能不是那么重要,但会尝试你的方法也排除“_VIEWSTATE”和这样的行:)
  • @Johan Helsén:请看我的编辑。我很确定您正在检查的字符串的长度是问题所在。我怀疑您的原始代码最终也会完成,但由于该模式非常宽松,因此在如此大的字符串中需要做很多工作。
  • 谢谢!这很好用。我测试了您的第一个解决方案,也删除了 _VIEMSTATE 行并将 RegexOptions.IgnorePatternWhitespace 添加到 regex.matches(),它工作正常:)
【解决方案3】:

从“函数评估超时”开始,我假设您正在调试器中执行此操作。调试器在方法需要多长时间方面有一些相当快的超时。并非所有事情都很快发生。我建议在代码中进行操作,存储结果,然后在调试器中查看该结果(即让对 Matches 的调用运行并在其后放置一个断点)。

现在,关于检测字符串是否会使 Matches 耗时较长;这有点黑艺术。您基本上必须执行某种输入验证。仅仅因为您从互联网上获得了一些价值,并不意味着该价值可以很好地与 Matches 配合使用。最终的验证逻辑取决于你;但是,从 rawHtmlLines 的长度开始可能会有用。 (即如果长度为 1000000 字节,匹配可能需要一段时间)但是,如果长度太长,您必须决定如何处理;例如给用户一个错误。

【讨论】:

  • 谢谢,将尝试将大字符串分解成更小的字符串并匹配它们。
猜你喜欢
  • 2018-07-31
  • 2019-05-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多