2012-07-26 57 views
6

我試圖使用下面的代碼拆分約32GB的文件,但我得到了memory exception如何使用C#拆分大文本文件(32 GB)

請推薦我使用C#拆分文件。

string[] splitFile = File.ReadAllLines(@"E:\\JKS\\ImportGenius\\0.txt"); 

int cycle = 1; 
int splitSize = Convert.ToInt32(txtNoOfLines.Text); 
var chunk = splitFile.Take(splitSize); 
var rem = splitFile.Skip(splitSize); 

while (chunk.Take(1).Count() > 0) 
{ 
    string filename = "file" + cycle.ToString() + ".txt"; 
    using (StreamWriter sw = new StreamWriter(filename)) 
    { 
     foreach (string line in chunk) 
     { 
    sw.WriteLine(line); 
     } 
    } 
    chunk = rem.Take(splitSize); 
    rem = rem.Skip(splitSize); 
    cycle++; 
} 
+6

這很容易修復。只要購買超過32GB的內存,您就可以讀取內存中的整個文件。 – Stilgar 2012-07-26 12:02:59

+0

我想你需要一個StreamReader – V4Vendetta 2012-07-26 12:03:48

+1

你只需將整個32GB文本文件一次讀入內存,這是完全不道德的。 – 2012-07-26 12:03:55

回答

11

那麼,首先你需要使用File.ReadLines(假設你使用的是.NET 4),以便它不會嘗試將整個事物讀入內存。然後,我只是不斷調用一種方法吐出「下一個」無論多行到一個新的文件:

int splitSize = Convert.ToInt32(txtNoOfLines.Text); 
using (var lineIterator = File.ReadLines(...).GetEnumerator()) 
{ 
    bool stillGoing = true; 
    for (int chunk = 0; stillGoing; chunk++) 
    { 
     stillGoing = WriteChunk(lineIterator, splitSize, chunk); 
    } 
} 

... 

private static bool WriteChunk(IEnumerator<string> lineIterator, 
           int splitSize, int chunk) 
{ 
    using (var writer = File.CreateText("file " + chunk + ".txt")) 
    { 
     for (int i = 0; i < splitSize; i++) 
     { 
      if (!lineIterator.MoveNext()) 
      { 
       return false; 
      } 
      writer.WriteLine(lineIterator.Current); 
     } 
    } 
    return true; 
} 
+0

感謝您的迴應喬恩。 – Jaffer 2012-07-30 06:08:18

+0

但是,當試圖使用你的代碼時,它顯示了迭代器「無法找到指令或程序集引用」。請幫我糾正這個問題 – Jaffer 2012-07-30 06:10:10

+0

@Jaffer:對不起,錯字 - 應該是'IEnumerator '。固定。 – 2012-07-30 06:16:05

0

這裏的問題是,你正在用File.ReadAllLines()一次性讀取整個文件的內容到內存中。你需要做的是打開一個文件流與File.OpenRead()和讀/寫較小的塊。

編輯:其實對於你的情況ReadLine顯然更好。查看其他答案。 :)

0

使用StreamReade r讀取文件,用StreamWriter寫入。

6

不要讀立即所有行到一個數組,但使用StremReader.ReadLine方法,如:

using (StreamReader sr = new StreamReader(@"E:\\JKS\\ImportGenius\\0.txt")) 
{ 
    while (sr.Peek() >= 0) 
    { 
     var fileLine = sr.ReadLine(); 
     //do something with line 
    } 
} 
+1

要挑剔,一條線可能> 32 GB – Guillaume 2012-07-26 12:16:04

+0

@Guillaume:考慮到OP的使用ReadAllLines我*假設文件的格式是我想的那個,按照行來劃分,而不是一個單行的大行。 – Tigran 2012-07-26 12:19:58

+0

,我們同意,如果文件不是來自可信對方,或者格式不明確,則可能是問題。而且,逐行復制文件效率不高。重用一個緩衝區(比如說32K)會提高很多。對於大於32 GB的文件,這可能也是一個問題。儘管如此,您的解決方案可能足以滿足賈弗需求。 – Guillaume 2012-07-26 12:38:35

3

而不是閱讀所有的文件在一次使用File.ReadAllLines,使用File.ReadLines在foreach循環讀取根據需要線。

foreach (var line in File.ReadLines(@"E:\\JKS\\ImportGenius\\0.txt")) 
{ 
    // Do something 
} 

編輯:在一個不相關的音符,你沒有前綴以「@」字符串時逃避你的反斜線。所以要麼寫"E:\\JKS\\ImportGenius\\0.txt"@"E:\JKS\ImportGenius\0.txt",但@"E:\\JKS\\ImportGenius\\0.txt"是多餘的。

3
File.ReadAllLines 

將讀取的整個文件到內存

要處理大文件,您只需要將現在需要的內容讀入內存,然後在完成後立即丟棄它。

一個更好的選擇是File.ReadLines,它返回一個懶惰的枚舉器,當你從枚舉器中獲得下一行時,數據只被讀入內存。如果您避免多次枚舉(例如,不要使用Count()),只會讀取部分文件。