4

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

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

File1_Row1,File2_Row1,File3_Row1,...

File1_Row2,File2_Row2,File3_Row2,...

File1_Row3,File2_Row3,File3_Row3,...

等等

到目前为止,我已经尝试了 2 个解决方案:

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

有没有人有更好的解决方案来快速读取这么多大文件?

4

4 回答 4

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);
    }
}

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

于 2013-07-05T07:12:00.547 回答
3

使用内存映射文件似乎适合您。不会对应用程序的内存造成压力的东西同时保持 IO 操作的良好性能。

这里完整的文档:内存映射文件

于 2013-07-05T06:35:52.100 回答
1

如果您的计算机上有足够的内存,我会使用 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 方法应该只使用以下示例代码,根据这些基准,这是读取文本文件的最快方法:

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

如果您需要在编写最终输出之前处理每个文件中的数据,请阅读本文以最快的方式执行此操作。

然后,您只需要一种方法即可根据需要将每个 string[] 的内容写入输出。

于 2014-07-19T07:20:38.683 回答
0

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

这与您的第二种方法不同,它是单线程的并且不读取特定行但始终读取下一行。

当然,如果文件的行数少于其他文件,则需要防错。

于 2013-07-05T06:59:05.860 回答