2012-07-26 18 views
6

Próbowałem podzielić plik na około 32 GB przy użyciu poniższego kodu, ale mam memory exception.Jak podzielić duży plik tekstowy (32 GB) przy użyciu C#

Proszę zasugerować, aby podzielić plik przy użyciu 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

Łatwo to naprawić. Po prostu kup więcej niż 32 GB RAM i będziesz mógł odczytać cały plik w pamięci. – Stilgar

+0

Zgaduję, że potrzebujesz StreamReadera – V4Vendetta

+1

Właśnie przeczytałeś cały plik tekstowy 32GB do pamięci, co jest całkowicie nieetyczne. –

Odpowiedz

11

Cóż, na początek trzeba użyć File.ReadLines (zakładając, że korzystasz z .NET 4), tak aby nie próbował odczytać całej rzeczy w pamięci. Wtedy po prostu wywoływałbym metodę wypluwania "następnego", jakkolwiek wiele linii do nowego pliku:

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

Dziękuję za odpowiedź Jon. – Jaffer

+0

Ale przy próbie użycia twojego kodu pokazuje to, że dla Iteratorów "Nie można znaleźć dyrektywy lub odwołania do zespołu". Proszę pomóż mi rozwiązać ten błąd: – Jaffer

+0

@Jaffer: Przepraszam, literówka - powinien to być "IEnumerator ". Naprawiony. –

0

Problem polega na tym, że czytasz zawartość całego pliku do pamięci jest jednocześnie z File.ReadAllLines(). Musisz otworzyć FileStream z File.OpenRead() i przeczytać/napisać mniejsze porcje.

Edytuj: Właściwie dla twojej sprawy ReadLine jest oczywiście lepsza. Zobacz inne odpowiedzi. :)

0

Użyj StreamReade r, aby przeczytać plik, napisz za pomocą StreamWriter.

6

Nie czytaj natychmiast wszystkie linie do tablicy, ale używać StremReader.ReadLine metody, jak:

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

Aby być nitpicking, linia może być> 32 GB – Guillaume

+0

@Guillaume: Biorąc pod uwagę, że OP używa ReadAllLines I * przypuszczam * format pliku jest taki, który mam być, thow podzielone przez linie, a nie jeden sinlgle big line. – Tigran

+0

i zgadzamy się, że może to być problem, jeśli plik nie pochodzi od zaufanego partnera lub jeśli format nie jest dobrze zdefiniowany. Co więcej, kopiowanie pliku wiersz po linii jest nieefektywne. Ponowne użycie bufora (na przykład 32K) znacznie się poprawi. Może to być również problem w przypadku plików> 32 GB. Jednak twoje rozwiązanie może być wystarczające dla potrzeb Jaffer. – Guillaume

3

Zamiast czytać cały plik na raz za pomocą File.ReadAllLines, użyj File.ReadLines w pętli foreach, aby przeczytać linie w razie potrzeby.

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

Edit: Na niepowiązanych uwaga, nie trzeba uciekać się swoimi backslashy gdy poprzedzając ciąg z „@”. Więc napisz "E:\\JKS\\ImportGenius\\0.txt" lub @"E:\JKS\ImportGenius\0.txt", ale @"E:\\JKS\\ImportGenius\\0.txt" jest zbędny.

3
File.ReadAllLines 

To będzie czytać cały plik do pamięci.

Aby pracować z dużymi plikami, musisz tylko przeczytać to, czego potrzebujesz teraz w pamięci, a następnie wyrzucić je, gdy tylko skończysz.

Lepszą opcją byłoby File.ReadLines, która zwraca leniwy moduł wyliczający, dane są odczytywane do pamięci dopiero po uzyskaniu następnego wiersza z modułu wyliczającego. Zapewniając unikanie wielokrotnych wyliczeń (np. Nie używaj Count()) tylko części pliku będą czytane.

Powiązane problemy