2013-07-05 87 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; 
     } 
} 

如果您在編寫最終輸出之前需要處理每個文件中的數據,請以最快的方式執行該操作。

然後,您只需要一種方法將內容寫入每個字符串[],然後根據需要輸出。

相關問題