我有一个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))
有谁知道为什么只有上面的正则表达式失败,为什么没有抛出异常?
答案 0 :(得分:2)
几点:
现在谈谈您的具体问题。为防止发生大量回溯,您可以尝试使用非回溯子表达式(?>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 标志,一般来说它会更快(只有正则表达式可能会运行几次以上才会更好。初始化要稍微长一些,您可能需要考虑将正则表达式作为静态只读字段移动到类作用域。)