我正在处理大小介于150 MB和250 MB之间的文件,我需要在匹配集合中找到的每个匹配项后附加一个换页(/f)字符。目前,每个匹配项的正则表达式如下:
Regex myreg = new Regex("ABC: DEF11-1111(.*?)MORE DATA(.*?)EVEN MORE DATA(.*?)\f", RegexOptions.Singleline);
并且我想修改文件中的每个匹配项(然后覆盖文件),使其成为以后可以使用更短的正则表达式找到的内容:
Regex myreg = new Regex("ABC: DEF11-1111(.*?)\f\f, RegexOptions.Singleline);
换句话说,我只想向在文件中找到的每个匹配项附加一个换页字符(\f)并保存它。
我看过很多关于替换文本的堆栈溢出的例子,但对于更大的文件就不多了。典型的例子包括:
- 使用streamreader将整个文件存储在一个字符串中,然后在该字符串中执行查找和替换。
- 将MatchCollection与File.ReadAllText()结合使用
- 逐行阅读文件并查找匹配项。
前两个选项的问题是它会消耗大量的内存,我担心程序是否能够处理所有这些。第三个选项的问题是我的正则表达式跨越了许多行,因此不会在一行中找到。我也看到了其他帖子,但它们涉及替换特定的文本字符串,而不是使用正则表达式。
对于我来说,将换页字符附加到文件中找到的每个匹配项,然后保存该文件的好方法是什么?
编辑:
根据一些建议,我尝试了一下StreamReader.ReadLine()。具体来说,我将读取一行,看看它是否与我的表达式匹配,然后基于该结果写入文件。如果它与表达式匹配,我将写入文件。如果它与表达式不匹配,我将把它附加到字符串,直到它与表达式匹配为止。如下所示:
正则表达式myreg =新正则表达式(“ABC:DEF 11 -1111(.?)更多数据(.?)甚至更多数据(.*?)\f”,正则表达式选项,单行);
//For storing/comparing our match.
string line, buildingmatch, match, whatremains;
buildingmatch = "";
match = "";
whatremains = "";
//For keep track of trailing bits after our match.
int matchlength = 0;
using (StreamWriter sw = new StreamWriter(destFile))
using (StreamReader sr = new StreamReader(srcFile))
{
//While we are still reading lines in the file...
while ((line = sr.ReadLine()) != null)
{
//Keep adding lines to buildingmatch until we can match the regular expression.
buildingmatch = buildingmatch + line + "\r\n";
if (myreg.IsMatch(buildingmatch)
{
match = myreg.Match(buildingmatch).Value;
matchlength = match.Lengh;
//Make sure we are not at the end of the file.
if (matchlength < buildingmatch.Length)
{
whatremains = buildingmatch.SubString(matchlength, buildingmatch.Length - matchlength);
}
sw.Write(match, + "\f\f");
buildingmatch = whatremains;
whatremains = "";
}
}
}
问题是,这花了大约55分钟来运行一个大约150 MB的文件。必须有更好的方法来做到这一点...
3条答案
按热度按时间qco9c6ql1#
如果您可以将整个字符串数据加载到单个字符串变量中,则无需首先进行匹配,然后在循环中将文本追加到匹配项。您可以使用单个
Regex.Replace
操作:string text = File.ReadAllText(srcFile);
-将srcFile
文件读取到text
变量(match
可能会混淆)myregex.Replace(text, "$&\f\f")
-将所有出现的myregex
匹配项替换为它们自己($&
是对整个匹配值的反向引用),同时在每个匹配项后立即附加两个\f
字符。z31licg02#
我能够在合理的时间内找到一个有效的解决方案;它可以在5分钟内处理我的整个150 MB文件。
首先,正如在注解中提到的,在每次迭代之后将字符串与正则表达式进行比较是一种浪费,相反,我从以下内容开始:
字符串最多可以容纳2GB的数据,所以虽然不是很理想,但我认为大约150 MB的数据存储在字符串中不会有什么损失。这样,与从文件中读取x行时检查一个匹配项不同,我可以一次检查文件中的所有匹配项!
接下来,我使用了这个:
因为我已经知道了文件的大小,我可以继续初始化我的stringbuilder。更不用说,如果你在一个字符串上做多个操作(我就是这样做的),使用string builder效率会高得多。从那里开始,只需要把表单提要附加到我的每个匹配项上。
最后,性能成本最高的部分:
初始化StreamWriter的方式很关键。通常,您只需将其声明为:
这对于大多数用例来说都很好,但是当你处理更大的文件时,这个问题就变得很明显了。当你这样声明的时候,你是在用一个4KB的默认缓冲区写文件。对于一个更小的文件,这是很好的。但是对于150 MB的文件?这将花费很长的时间。所以我通过将缓冲区改为大约5 MB来纠正这个问题。
我发现这个资源确实帮助我理解了如何更有效地写入文件:https://www.jeremyshanks.com/fastest-way-to-write-text-files-to-disk-in-c/
希望这对下一个人也有帮助。
2jcobegt3#
在C#中处理大型文本文件并需要执行搜索和替换操作时,可以考虑使用几种方法来优化性能。
一种方法是使用内存Map文件,内存Map文件允许您访问大型文件,就好像它们是内存数组一样,这比使用标准文件I/O更有效。要使用内存Map文件,可以使用C#中的
MemoryMappedFile
类。如果内存Map文件是一个可行的选择,那么它们可以提供比传统阅读方法更快的文件内容访问。