基本上我想做的是在单个字符串上运行多个(15-25)正则表达式替换,以实现最佳的内存管理。
概述:
通过ftp附加到StringBuilder
以获取非常大的字符串,将文本文件(有时是html)流式传输。文件大小范围从300KB到30MB。
正则表达式是半复杂的,但需要文件的多行(例如,识别书籍的各个部分),因此任意破坏字符串,或者在每个下载循环上运行替换都是不可能的。
替换样本:
Regex re = new Regex("<A.*?>Table of Contents</A>", RegexOptions.IgnoreCase);
source = re.Replace(source, "");
每次运行更换内存天空火箭,我知道这是因为字符串在C#中是不可变的,它需要复制 - 即使我调用GC.Collect()
它仍然没有足够的帮助30MB文件。
有关更好的接近方法的建议,或使用常量内存执行多个正则表达式替换的方法(制作2个副本(内存为60MB),执行搜索,将副本丢弃回30MB)?
似乎没有一个简单的答案,但是对于未来的人来说,我最终使用了以下所有答案的组合来使其达到可接受的状态:
如果可能的话,将字符串拆分成块,请参阅manojlds的答案,了解正在读取文件的方法 - 寻找合适的终点。
如果您不能在流式传输时进行拆分,请至少在以后拆分 - 请参阅ChrisWue的一些外部工具的答案,这些工具可能有助于此流程管道传输到文件。
优化正则表达式,避免贪婪的运算符,尽量限制引擎必须做的事情 - 请参阅Sylverdrag的回答。
尽可能结合正则表达式,这减少了正则表达式不相互依赖时的替换次数(在这种情况下用于清除错误输入) - 请参阅Brian Reichle对代码示例的回答。 / p>
谢谢大家!
答案 0 :(得分:2)
看看这篇文章,讨论使用正则表达式搜索流而不是存储在消耗内存的字符串中:
答案 1 :(得分:2)
根据RegEx的性质,您可以将它们组合成单个正则表达式,并使用带有MatchEvaluator委托的Replace()重载来确定匹配字符串的替换。
Regex re = new Regex("First Pattern|Second Pattern|Super(Mega)*Delux", RegexOptions.IgnoreCase);
source = re.Replace(source, delegate(Match m)
{
string value = m.Value;
if(value.Equals("first pattern", StringComparison.OrdinalIgnoreCase)
{
return "1st";
}
else if(value.Equals("second pattern", StringComparison.OrdinalIgnoreCase)
{
return "2nd";
}
else
{
return "";
}
});
当然,如果后面的模式需要能够匹配早期替换的结果,那么这就会崩溃。
答案 2 :(得分:2)
我的情况非常相似。
使用正则表达式的编译选项:
Source = Regex.Replace(source, pattern, replace, RegexOptions.Compiled);
根据您的情况,它可以在速度方面产生重大影响。
不是完整的解决方案,尤其是对于大于3-4 Mb的文件。
如果你决定应该运行哪个正则表达式(不是我的情况),你应该尽可能地优化正则表达式,避免代价高昂的操作。例如,避免不合理的操作员,避免向前看并向后看。
而不是使用:
<a.*?>xxx
使用
<a[^<>]*>xxx
原因是不合格的操作符强制正则表达式引擎检查与表达式的其余部分相比的每个字符,而[^&lt;&gt;]仅需要将当前字符与&lt;和&gt;并在条件匹配时立即停止。在一个大文件中,这可以在半秒和应用程序冻结之间产生差异。
它并没有完全解决问题,但它应该有所帮助。
答案 3 :(得分:1)
假设您加载的文档具有某种类型的结构,您可能最好编写解析器以将文档放入结构化形式,将大字符串分成多个块,然后再操作那个结构。
大字符串的一个问题是超过85,000字节的对象被认为是大对象,并且放在没有压缩的大对象堆上,它可能导致意外的内存不足。
另一种选择是通过sed
或awk
之类的外部工具来管道。