使用 streamreader/streamwriter 在 C# 中逐行写入文件非常慢
Writing file line by line in C# very slow using streamreader/streamwriter
我编写了一个 Winform 应用程序,它读取文本文件的每一行,使用 RegEx 搜索并替换该行,然后写回一个新文件。我选择了 "line by line" 方法,因为有些文件太大而无法加载到内存中。
我正在使用 BackgroundWorker 对象,因此 UI 可以随着作业的进度进行更新。下面是处理文件中行的读取和输出的代码(为简洁起见省略了部分)。
public void bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
// Details of obtaining file paths omitted for brevity
int totalLineCount = File.ReadLines(inputFilePath).Count();
using (StreamReader sr = new StreamReader(inputFilePath))
{
int currentLine = 0;
String line;
while ((line = sr.ReadLine()) != null)
{
currentLine++;
// Match and replace contents of the line
// omitted for brevity
if (currentLine % 100 == 0)
{
int percentComplete = (currentLine * 100 / totalLineCount);
bgWorker.ReportProgress(percentComplete);
}
using (FileStream fs = new FileStream(outputFilePath, FileMode.Append, FileAccess.Write))
using (StreamWriter sw = new StreamWriter(fs))
{
sw.WriteLine(line);
}
}
}
}
我正在处理的一些文件非常大(8 GB,1.32 亿行)。该过程需要很长时间(一个 2 GB 的文件大约需要 9 个小时才能完成)。它看起来在 58 KB/sec 左右工作。这是预期的还是应该加快进程?
不要在每次循环迭代时关闭并重新打开写入文件,只需在文件循环外打开写入器即可。这应该会提高性能,因为编写器不再需要在每次循环迭代时都查找文件末尾。
另外File.ReadLines(inputFilePath).Count();
导致您读取输入文件两次并且可能会花费大量时间。而不是基于线的百分比计算基于流位置的百分比。
public void bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
// Details of obtaining file paths omitted for brevity
using (StreamWriter sw = new StreamWriter(outputFilePath, true)) //You can use this constructor instead of FileStream, it does the same operation.
using (StreamReader sr = new StreamReader(inputFilePath))
{
int lastPercentage = 0;
String line;
while ((line = sr.ReadLine()) != null)
{
// Match and replace contents of the line
// omitted for brevity
//Poisition and length are longs not ints so we need to cast at the end.
int currentPercentage = (int)(sr.BaseStream.Position * 100L / sr.BaseStream.Length);
if (lastPercentage != currentPercentage )
{
bgWorker.ReportProgress(currentPercentage );
lastPercentage = currentPercentage;
}
sw.WriteLine(line);
}
}
}
除此之外,您还需要展示 Match and replace contents of the line omitted for brevity
做了什么,我猜这就是您速度慢的原因。 运行 对您的代码进行探查,查看花费最多时间的地方并将您的精力集中在那里。
遵循这个过程:
- 实例化reader和编写器
- 遍历行,执行接下来的两个步骤
- 循环换行
- 循环写入更改行
- 处置reader和writer
这应该比您在每一行循环中实例化编写器快很多。
我将很快附加一个代码示例。看起来有人在代码示例上抢先一步 - 请参阅@Scott Chamberlain 的回答。
删除顶部的 ReadAllLines 方法,因为读取整个文件只是为了获取行数。
我编写了一个 Winform 应用程序,它读取文本文件的每一行,使用 RegEx 搜索并替换该行,然后写回一个新文件。我选择了 "line by line" 方法,因为有些文件太大而无法加载到内存中。
我正在使用 BackgroundWorker 对象,因此 UI 可以随着作业的进度进行更新。下面是处理文件中行的读取和输出的代码(为简洁起见省略了部分)。
public void bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
// Details of obtaining file paths omitted for brevity
int totalLineCount = File.ReadLines(inputFilePath).Count();
using (StreamReader sr = new StreamReader(inputFilePath))
{
int currentLine = 0;
String line;
while ((line = sr.ReadLine()) != null)
{
currentLine++;
// Match and replace contents of the line
// omitted for brevity
if (currentLine % 100 == 0)
{
int percentComplete = (currentLine * 100 / totalLineCount);
bgWorker.ReportProgress(percentComplete);
}
using (FileStream fs = new FileStream(outputFilePath, FileMode.Append, FileAccess.Write))
using (StreamWriter sw = new StreamWriter(fs))
{
sw.WriteLine(line);
}
}
}
}
我正在处理的一些文件非常大(8 GB,1.32 亿行)。该过程需要很长时间(一个 2 GB 的文件大约需要 9 个小时才能完成)。它看起来在 58 KB/sec 左右工作。这是预期的还是应该加快进程?
不要在每次循环迭代时关闭并重新打开写入文件,只需在文件循环外打开写入器即可。这应该会提高性能,因为编写器不再需要在每次循环迭代时都查找文件末尾。
另外File.ReadLines(inputFilePath).Count();
导致您读取输入文件两次并且可能会花费大量时间。而不是基于线的百分比计算基于流位置的百分比。
public void bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
// Details of obtaining file paths omitted for brevity
using (StreamWriter sw = new StreamWriter(outputFilePath, true)) //You can use this constructor instead of FileStream, it does the same operation.
using (StreamReader sr = new StreamReader(inputFilePath))
{
int lastPercentage = 0;
String line;
while ((line = sr.ReadLine()) != null)
{
// Match and replace contents of the line
// omitted for brevity
//Poisition and length are longs not ints so we need to cast at the end.
int currentPercentage = (int)(sr.BaseStream.Position * 100L / sr.BaseStream.Length);
if (lastPercentage != currentPercentage )
{
bgWorker.ReportProgress(currentPercentage );
lastPercentage = currentPercentage;
}
sw.WriteLine(line);
}
}
}
除此之外,您还需要展示 Match and replace contents of the line omitted for brevity
做了什么,我猜这就是您速度慢的原因。 运行 对您的代码进行探查,查看花费最多时间的地方并将您的精力集中在那里。
遵循这个过程:
- 实例化reader和编写器
- 遍历行,执行接下来的两个步骤
- 循环换行
- 循环写入更改行
- 处置reader和writer
这应该比您在每一行循环中实例化编写器快很多。
我将很快附加一个代码示例。看起来有人在代码示例上抢先一步 - 请参阅@Scott Chamberlain 的回答。
删除顶部的 ReadAllLines 方法,因为读取整个文件只是为了获取行数。