使用streamreader / streamwriter

时间:2015-06-30 16:08:17

标签: c# streamreader streamwriter

我编写了一个Winform应用程序,它读取文本文件的每一行,使用行上的RegEx进行搜索和替换,然后将其写回新文件。我选择了“逐行”方法,因为有些文件太大而无法加载到内存中。

我正在使用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 /秒的速度运行。这是预期的还是应该加快进程?

3 个答案:

答案 0 :(得分:14)

每次循环迭代都不要关闭并重新打开写入文件,只需在文件循环外打开编写器。这应该可以提高性能,因为编写器不再需要在每次循环迭代中寻找文件的末尾。

同样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所做的事情,因为我猜这是您的缓慢来源。在您的代码上运行一个分析器,看看它花费的时间最多,并集中精力。

答案 1 :(得分:1)

请遵循以下流程:

  1. 实例化读者和作家
  2. 循环线,完成接下来的两个步骤
  3. 在循环更改行
  4. 在循环中写入更改的行
  5. 处理读者和作者
  6. 这比你在每个行循环上实例化编写器要快得多。

    我将很快将其附加到代码示例中。看起来有人打败了我的代码样本 - 请参阅@Scott Chamberlain的回答。

答案 2 :(得分:0)

删除顶部的ReadAllLines方法,读取整个文件只是为了得到行数。