Frage

Ich bin mit C # ein ~ 120 MB Nur-Text-CSV-Datei zu lesen. Am Anfang habe ich die Analyse durch Zeile-für-Zeile zu lesen, aber vor kurzem festgestellt, dass der gesamten Dateiinhalt in dem Speicher einzulesen ersten mehrfach schneller war. Die Analyse ist bereits ziemlich langsam, weil die CSV Komma eingebettet in Anführungszeichen hat, was bedeutet, ich habe einen regulären Ausdruck Split verwenden. Dies ist die einzige, die ich gefunden habe, die zuverlässig funktioniert:

string[] fields = Regex.Split(line, 
@",(?!(?<=(?:^|,)\s*\x22(?:[^\x22]|\x22\x22|\\\x22)*,)
(?:[^\x22]|\x22\x22|\\\x22)*\x22\s*(?:,|$))");
// from http://regexlib.com/REDetails.aspx?regexp_id=621

Um die Analyse nach dem Lesen Sie den gesamten Inhalt in den Speicher zu tun, kann ich einen String Split auf dem Newline-Zeichen um ein Array zu erhalten, jede Zeile enthält. Allerdings, wenn ich dies tun, auf der 120 MB-Datei, erhalte ich eine System.OutOfMemoryException. Warum dauert es so schnell aus dem Speicher ausgeführt werden, wenn der Computer hat 4 GB RAM? Gibt es einen besseren Weg, um eine komplizierte CSV schnell zu analysieren?

War es hilfreich?

Lösung

Sie können einen OutOfMemoryException bekommen für grundsätzlich jede Größe der Zuordnung. Wenn Sie ein Stück Speicher reservieren Sie wirklich für ein kontinuierliches Stück Erinnerung an die gewünschte Größe zu fragen. Wenn das nicht eingelöst werden kann, erhalten Sie eine OutOfMemoryException sehen.

Sie sollten sich auch bewusst sein, dass, wenn Sie laufen 64-Bit-Windows Ihre 4 GB RAM in 2 GB Kernel-Space und 2 GB Benutzerraum geteilt ist, so dass Ihre .NET-Anwendung kann nicht mehr zugreifen, die 2 GB pro Standard.

Wenn String-Operationen in .NET tun Sie riskieren eine Menge temporäre Strings aufgrund der Tatsache, dass die Schaffung .NET Strings unveränderlich sind. Daher können Sie die Speichernutzung steigt dramatisch sehen.

Andere Tipps

Sie nicht Ihre eigenen Parser rollen, wenn Sie zu haben. Ich habe Glück gehabt mit dieser:

eine schnelle CSV Reader

Wenn nichts anderes können Sie unter der Haube schauen und sehen, wie jemand anderes tut es.

Wenn Sie die gesamte Datei in einen String gelesen haben, sollten Sie wahrscheinlich ein String .

StringReader reader = new StringReader(fileContents);
string line;
while ((line = reader.ReadLine()) != null) {
    // Process line
}

Dies sollte roughtly das gleiche sein wie mit dem Unterschied, aus einem Datei-Streaming, dass der Inhalt bereits im Speicher ist.

Bearbeiten nach dem Test

die oben mit einer 140MB-Datei versucht, wo die Verarbeitung mit line.Length Inkrementieren Länge variabel bestand. Dies dauerte etwa 1,6 Sekunden auf meinem Computer. Danach habe ich versucht, die folgenden:

System.IO.StreamReader reader = new StreamReader("D:\\test.txt");
long length = 0;
string line;
while ((line = reader.ReadLine()) != null)
    length += line.Length;

Das Ergebnis war etwa 1 Sekunde.

Natürlich können Sie Ihre Laufleistung variieren, vor allem, wenn Sie von einem Netzlaufwerk lesen oder Ihre Verarbeitung dauert lange genug, um die Festplatte woanders zu suchen. Aber auch, wenn Sie Filestream verwenden, die Datei zu lesen und Sie sind nicht zu puffern. Stream bietet Pufferung die das Lesen stark verbessert.

Sie können nicht in der Lage sein, ein einzelnes Objekt mit so vielen zusammenhängenden Speichern zuweisen, noch sollten Sie erwarten zu können. Streaming ist der normale Weg, dies zu tun, aber du hast recht, es könnte langsamer sein (obwohl ich nicht glaube, es in der Regel recht sein soll, dass viel langsamer.)

Als Kompromiss könnte man einen größeren Teil der Datei versuchen zu lesen (aber immer noch nicht die ganze Sache) auf einmal, mit einer Funktion wie StreamReader.ReadBlock() und Verarbeiten jedes Teil in der Reihe.

Wie andere Plakate sagen, die OutOfMemory ist, weil es nicht einen zusammenhängenden Teil des Speichers von der gewünschten Größe finden.

Allerdings sagen Sie, dass die Zeile Parsing tut Zeile mehrmals schneller war als sie alle auf einmal zu lesen und dann die Verarbeitung zu tun. Dies macht nur Sinn, wenn man den naiven Ansatz zu tun Blockierung verfolgten liest, zB (in Pseudo-Code):

while(! file.eof() )
{
    string line = file.ReadLine();
    ProcessLine(line);
}

Sie sollten stattdessen verwenden Streaming, wo Ihr Strom durch Schreiben in gefüllt ist () ruft von einem anderen Thread, der die Datei liest, so dass die Datei Lese nicht durch was auch immer Ihre Process () blockiert tut, und umgekehrt. Das sollte mit der Leistung des Lesens die gesamte Datei auf einmal auf Stück sein und dann die Verarbeitung zu tun.

Sie sollten wahrscheinlich versuchen, den CLR Profiler Ihre aktuelle Speichernutzung zu bestimmen. Es könnte sein, dass es Speichergrenzen außer Ihrem System-RAM. Zum Beispiel, wenn dies eine IIS-Anwendung ist, wird Ihr Gedächtnis durch die Anwendungspools beschränkt.

Mit diesem Profil Informationen, die Sie feststellen, dass Sie eine skalierbare Technik wie das Streaming der CSV-Datei verwenden müssen, die Sie ursprünglich versucht.

Sie laufen auf dem Stapel aus der Erinnerung, nicht der Heap.

Sie könnten versuchen, Re-Factoring Ihre App, so dass Sie die Eingabe eher in überschaubaren „Brocken“ von Daten sind die Verarbeitung als zu einem Zeitpunkt 120MB verarbeiten.

Ich stimme mit den meisten jeder hier, müssen Sie Streaming verwenden.

Ich weiß nicht, ob jemand bisher gesagt hat, Sie aber bei einer exstention Methode aussehen sollten.

Und ich weiß, das ist sicher, die Hände nach unten, um die besten CSV Splitting-Technik auf .NET / CLR ist diese

Diese Technik erzeugt me + 10GB XML-Ausgabe der vom Eingang CSV, einschließlich exstensive Eingangsfilter und alle, schneller als alles andere, was ich gesehen habe.

Sie sollten ein Stück in einen Puffer gelesen und daran arbeiten. Dann eine weitere Brocken lesen und so weiter.

Es gibt viele Bibliotheken gibt, die dies für Sie effizient tun. Ich behaupte, eine namens CsvHelper . Es gibt viele Grenzfälle, die Sie behandeln müssen, wie zum Beispiel, wenn ein Komma oder Zeilenende ist in der Mitte eines Feldes.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top