2013-07-05 42 views
3

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

例如,主文件应该是这个样子:

File1_Row1,File2_Row1,File3_Row1,... 

File1_Row2,File2_Row2,File3_Row2,... 

File1_Row3,File2_Row3,File3_Row3,... 

我已经试过2个解决方案迄今:

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

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

+8

“...喜欢记事本,而不是微软的产品......” - 我讨厌把它分解给你,但记事本是微软的产品 –

+1

......而文本文件是文本文件。换句话说,没有'NotePadFileStream'。 – ChiefTwoPencils

回答

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

我一直认为所有的输入文件具有相同的行数,但是你应该加时保留至少一个文件给你数据读取的逻辑。

+0

谢谢你是个天才!它效果很好。 – jmm1487

3

使用Memory Mapped文件似乎是什么适合你。某些不会对您的应用程序的内存施加压力,以保持IO操作的良好性能。

这里完整的文档:Memory-Mapped Files

+0

作为一个答案(而不是评论),这有点像回答这个问题,*“你怎么盖房子?”*带*“使用砖块。在这个链接上有一些好的砖块指南”* 。正如[mike z的答案](http://stackoverflow.com/a/17483103)所示,至少有一种方法可以使用OP已知的工具来回答这个问题;这个问题更多的是关于如何使用这些工具而不是使用哪些工具。 – shambulator

+1

@shambulator:在这里看不到任何无效的东西。我们所谈论的技术并不能用简洁的答案来解释。我可以复制/粘贴由文档提供的代码,但认为文档可以更好地呈现我们正在讨论的功能的详细解释。 – Tigran

+0

但据我了解msdn文章,内存映射文件仍然需要开发人员处理内存溢出,对吧? –

0

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

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

当然,如果文件行数少于其他文件,您需要证明是错误的。

+0

我假设这不会比线程方法快得多。虽然它消除了多个竞争硬盘访问的线程,但它仍然有很多随机I/O。 – Chris

+0

不超过这个:http://stackoverflow.com/a/17483103/564226 - 它可以通过打开缓冲区或使用显式BufferedStream文件来改善。顺便说一句,我有一个印象,即OP的线程方法没有读取下一行,但总是一直到某个行号。 – JeffRSon

+1

@jeffrson IIRC StreamReader/Writer默认被缓冲。 –

3

如果您的计算机上有足够的内存,我会使用并行。调用构建体和读取每个文件到一个预先分配的阵列,例如:

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

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

然后,您只需要一种方法将内容写入每个字符串[],然后根据需要输出。

相关问题