读取多个非常大的文件的最佳方法

时间:2013-07-05 06:28:22

标签: c# performance file file-io text-files

我需要帮助找出最快的方法来读取每个文件中超过500,000行的大约80个文件,并写入一个主文件,每个输入文件的行作为主列中的列。主文件必须写入文本编辑器,如记事本,而不是Microsoft产品,因为它们无法处理行数。

例如,主文件应如下所示:

File1_Row1,File2_Row1,File3_Row1,...

File1_Row2,File2_Row2,File3_Row2,...

File1_Row3,File2_Row3,File3_Row3,...

到目前为止,我尝试了两种解决方案:

  1. 创建一个锯齿状数组以将每个文件的内容保存到一个数组中,然后一旦读取所有文件中的所有行,就编写主文件。此解决方案的问题是Windows操作系统内存会引发错误,即使用了太多的虚拟内存。
  2. 为读取特定行号的80个文件中的每个文件动态创建一个阅读器线程,一旦所有线程读完一行,将这些值组合并写入文件,并对所有文件中的每一行重复。这个解决方案的问题在于它非常慢。
  3. 有没有人能够以更快的方式阅读这么多大文件?

4 个答案:

答案 0 :(得分:5)

最好的方法是打开输入文件,每个输入文件StreamReader,输出文件StreamWriter。然后循环浏览每个阅读器并读取一行并将其写入主文件。这样你一次只能加载一行,所以应该有最小的内存压力。我能够在37秒内复制80~500,000行文件。一个例子:

using System;
using System.Collections.Generic;
using System.IO;
using System.Diagnostics;

class MainClass
{
    static string[] fileNames = Enumerable.Range(1, 80).Select(i => string.Format("file{0}.txt", i)).ToArray();

    public static void Main(string[] args)
    {
        var stopwatch = Stopwatch.StartNew();
        List<StreamReader> readers = fileNames.Select(f => new StreamReader(f)).ToList();

        try
        {
            using (StreamWriter writer = new StreamWriter("master.txt"))
            {
                string line = null;
                do
                {
                    for(int i = 0; i < readers.Count; i++)
                    {
                        if ((line = readers[i].ReadLine()) != null)
                        {
                            writer.Write(line);
                        }
                        if (i < readers.Count - 1)
                            writer.Write(",");
                    }
                    writer.WriteLine();
                } while (line != null);
            }
        }
        finally
        {
            foreach(var reader in readers)
            {
                reader.Close();
            }
        }
        Console.WriteLine("Elapsed {0} ms", stopwatch.ElapsedMilliseconds);
    }
}

我假设所有输入文件的行数都相同,但是当至少有一个文件为您提供数据时,您应该添加逻辑以保持读取。

答案 1 :(得分:3)

使用Memory Mapped文件似乎适合您。对您的应用程序的内存不施加压力的东西当代在IO操作中保持良好的性能。

此处有完整的文档:Memory-Mapped Files

答案 2 :(得分:3)

如果计算机上有足够的内存,我会使用Parallel.Invoke构造并将每个文件读入预先分配的数组,例如:

string[] file1lines = new string[some value];
string[] file2lines = new string[some value];
string[] file3lines = new string[some value];

Parallel.Invoke(
() =>
{
   ReadMyFile(file1,file1lines);
},
() =>
{
   ReadMyFile(file2,file2lines);
},
() =>
{
   ReadMyFile(file3,file3lines);
}
);

每个ReadMyFile方法应该使用以下示例代码,according to these benchmarks最快的方式来读取文本文件:

int x = 0;
using (StreamReader sr = File.OpenText(fileName))
{
        while ((file1lines[x] = sr.ReadLine()) != null)
        {
               x += 1;
        }
}

如果您需要在编写最终输出之前操作每个文件中的数据,read this article以最快的方式执行此操作。

然后你只需要一种方法将内容写入每个字符串[]到你想要的输出。

答案 3 :(得分:0)

有一组打开的文件句柄。循环遍历此数组并从每个文件中读取一行到字符串数组中。然后将此数组合并到主文件中,在最后添加换行符。

这与你的第二种方法不同,它是单线程的,不会读取特定的行,但总是下一行。

当然,如果文件的行数少于其他文件,则需要进行错误验证。