为什么这个正则表达式中途死了?

时间:2012-03-21 13:22:13

标签: c# regex

我有一个html文档(大小> 3mb)在其正文中包含1k +空char 0。

我有一个文本处理程序,它将加载html,使用正则表达式过滤掉所有标签,例如:

 input = Regex.Replace(input, "<([^^]*?)>", " ", RegexOptions.Multiline | RegexOptions.IgnoreCase);

并使用以下内容检索句子:

     Regex rx = new Regex(@"(\S.+?[.!?])(?=\s+|$)", RegexOptions.Multiline | RegexOptions.IgnoreCase);

            foreach (Match match in rx.Matches(input))
            {
               //do somthing
            }

到目前为止,标签过滤工作正常,但除非我提前删除所有字符0,否则上面的过程会失败。

 input =input.Replace(string.Format("{0}",(char)(0)),"");

“失败”意味着工作线程冻结在行:foreach (Match match in rx.Matches(input))

有谁知道为什么只有上面的正则表达式失败,为什么没有抛出异常?

For Reference

2 个答案:

答案 0 :(得分:2)

几点:

  1. 正则表达式不是处理HTML文件的好工具。对于正则表达式解决方案,HTML规则通常过于复杂。 HTML Agility Pack将是一个更好的解决方案,可以删除HTML标记并获取文本。
  2. catastrophic backtracking这样的东西可以看似冻结你的正则表达式。我最近created a video展示了在这方面要注意的一些常见错误。
  3. 现在谈谈您的具体问题。为防止发生大量回溯,您可以尝试使用非回溯子表达式(?>pattern)

    (?>\S.+?[.!?])(?=\s+|$)
    

    只要子表达式(或组)内的回溯不是实现最终结果所必需的,就可以使用它。在某些情况下,它会大大提高性能。

    除此之外,我建议删除IgnoreCase选项,因为这只会减慢速度,而不是你正在做的事情。然后使用Yorye提到的Compiled选项。此外,有时(取决于你的正则表达式的性质)你可以真正受益于使用RightToLeft选项,我认为这将是这种情况。在我的测试RightToLeft中,速度提高了60倍。

    所有这一切,你可以试试这个......

    var MyRegex = new Regex(@"(?>\S.+?[.!?])(?=\s+|$)", RegexOptions.Compiled | RegexOptions.Multiline | RegexOptions.RightToLeft);
    

    如上所述初始化一次Regex会很有帮助,然后您可以多次重复使用MyRegex。当您考虑Compiled选项带来的额外开销时,尤其如此。


    <强>更新

    我对此做了一些测试,发现RightToLeft选项实际上是在破坏它。而且,如果你只处理一堆句子,你的正则表达式通常会非常快。当句子真的很长(或者它们永远不会结束)时,它会很慢。

    所以你可以设置一个最大句子长度来强制正则表达式尽快放弃......

    \S.{2,250}[.!?](?=\s|$)
    

    另一件可能有用的事情是将\n(换行符)添加到标点符号列表...

    \S.{2,250}[.!?\n](?=\s|$)
    

    更新#2

    我发现了另一种更快的方法。而不是匹配句子,split标点符号的效率要高得多。在我上面列出的案例(非常长的句子)中尤其如此。

    你可以用这个分开......

    (?<=[.!?])\s+
    

    然后你最终得到的是一系列句子。

答案 1 :(得分:1)

你应该在特殊的正则表达式字符(例如。或?或)之前添加'\',以防你想要实际的字符(否则 - 我不理解你的第二个正则表达式)。

其次,它可能实际上并没有被卡住,因为文件很庞大需要很长时间。尝试用以下代码替换foreach循环:

var match = rx.Match(input);

while (match.Success)
{
    // do something

    match = match.NextMatch();
}

此外,在初始化正则表达式时添加 RegexOptions.Compiled 标志,一般来说它会更快(只有正则表达式可能会运行几次以上才会更好。初始化要稍微长一些,您可能需要考虑将正则表达式作为静态只读字段移动到类作用域。)