Der effizienteste Weg, um eine große csv in .NET zu verarbeiten

8

Verzeihen Sie meine Ratlosigkeit, aber ich brauche nur eine Anleitung und ich kann keine andere Frage finden, die das beantwortet. Ich habe eine ziemlich große CSV-Datei (~ 300k Zeilen) und ich muss für eine bestimmte Eingabe feststellen, ob eine Zeile in der CSV mit dieser Eingabe beginnt. Ich habe die CSV alphabetisch sortiert, aber ich weiß nicht:

1) Wie verarbeite ich die Zeilen in der csv - sollte ich sie als Liste / Sammlung einlesen, oder verwende OLEDB oder eine eingebettete Datenbank oder etwas anderes?

2) wie man etwas effizient aus einer alphabetischen Liste findet (mit der Tatsache, dass es sortiert ist, um die Dinge zu beschleunigen, anstatt die ganze Liste zu durchsuchen)

    
user1981003 15.01.2013, 16:47
quelle

10 Antworten

7

Sie geben nicht genug Details, um Ihnen eine konkrete Antwort zu geben, aber ...

Wenn sich die CSV-Datei häufig ändert, dann verwenden Sie OLEDB und ändern Sie einfach die SQL-Abfrage basierend auf Ihrer Eingabe.

%Vor%

Wenn sich die CSV-Datei nicht häufig ändert und Sie viele "Abfragen" dagegen ausführen, laden Sie sie einmal in den Speicher und durchsuchen Sie sie jedes Mal schnell.

Wenn Sie möchten, dass Ihre Suche genau auf eine Spalte passt, verwenden Sie ein Dictionary, in dem der Schlüssel die Spalte ist, die Sie abgleichen möchten, und der Wert die Zeilendaten ist.

%Vor%

Wenn Sie möchten, dass Ihre Suche eine teilweise Übereinstimmung wie StartsWith ist, dann haben Sie 1 Array, das Ihre durchsuchbaren Daten enthält (zB: erste Spalte) und eine andere Liste oder ein Array mit Ihren Zeilendaten. Dann benutze C # 's eingebaute binäre Suche Ссылка

%Vor%

HINWEIS Code wurde innerhalb des Browserfensters geschrieben und kann Syntaxfehler enthalten, da er nicht getestet wurde.

    
Louis Ricci 15.01.2013, 17:13
quelle
4

Wenn Sie die Daten im Speicher zwischenspeichern können und Sie nur die Liste nach einer Primärschlüsselspalte durchsuchen müssen, würde ich empfehlen, die Daten als Dictionary -Objekt im Speicher zu speichern. Die Klasse Dictionary speichert die Daten als Schlüssel / Wert-Paare in einer Hash-Tabelle. Sie können die Primärschlüsselspalte als Schlüssel im Wörterbuch verwenden und dann den Rest der Spalten als Wert im Wörterbuch verwenden. Das Nachschlagen von Elementen nach Schlüssel in einer Hash-Tabelle ist normalerweise sehr schnell.

Sie können die Daten beispielsweise folgendermaßen in ein Wörterbuch laden:

%Vor%

Und dann könnten Sie die Daten für jedes Element wie folgt erhalten:

%Vor%     
Steven Doggart 15.01.2013 16:56
quelle
4

Wenn Sie es nur einmal pro Programmlauf machen, scheint das ziemlich schnell zu sein. (Aktualisiert, um StreamReader anstelle von FileStream basierend auf den Kommentaren unten zu verwenden)

%Vor%

Dies wird in 0,007 Sekunden für eine 600.000-Datensatz-Testdatei mit 50 MB ausgeführt. Im Vergleich dazu dauert ein Datei-Scan durchschnittlich mehr als eine halbe Sekunde, je nachdem, wo sich der Datensatz befindet. (ein 100-facher Unterschied)

Offensichtlich, wenn Sie es mehr als einmal tun, wird Caching die Dinge beschleunigen. Eine einfache Möglichkeit zum partiellen Caching besteht darin, den StreamReader offen zu halten und ihn erneut zu verwenden. Setzen Sie einfach jedes Mal min und max zurück. Dadurch können Sie jederzeit 50 MB im Speicher halten.

BEARBEITEN: Der kniki02-Fix wurde hinzugefügt.

    
user645280 15.01.2013 19:49
quelle
3

Gegeben die CSV ist sortiert - wenn Sie die gesamte Sache in den Speicher laden können (Wenn die einzige Verarbeitung, die Sie tun müssen, ist .StartsWith () auf jeder Zeile) - Sie können ein Binary Suche für außergewöhnlich schnelle Suche.

Vielleicht so etwas (NICHT GETESTET!):

%Vor%

...

%Vor%     
Dave Bish 15.01.2013 16:53
quelle
1

Wenn sich Ihre Datei im Speicher befindet (z. B. weil Sie sortiert haben) und Sie sie als Array mit Strings (Zeilen) beibehalten, können Sie eine einfache halbierte Suche Methode. Sie können mit dem Code zu dieser Frage auf CodeReview beginnen, ändern Sie einfach den Vergleich mit string anstelle von int und nur den Anfang jeder Zeile überprüfen.

Wenn Sie die Datei jedes Mal neu lesen müssen, weil sie geändert oder von einem anderen Programm gespeichert / sortiert werden kann, ist der einfachste Algorithmus der beste:

%Vor%

Natürlich können Sie die gesamte Datei im Speicher lesen (um LINQ oder List<T>.BinarySearch() zu verwenden), aber das ist weit von optimal (Sie werden alles lesen selbst wenn Sie vielleicht nur ein paar Zeilen untersuchen müssen) und die Datei selbst könnte sogar zu groß sein.

Wenn Sie wirklich etwas mehr brauchen und Sie Ihre Datei aufgrund der Sortierung nicht im Speicher haben (aber Sie sollten profile Ihre tatsächliche Leistung im Vergleich zu Ihren Anforderungen haben) um einen besseren Suchalgorithmus zu implementieren, zum Beispiel den Boyer-Moore-Algorithmus .

    
Adriano Repetti 15.01.2013 16:56
quelle
1

OP erklärt wirklich muss nur auf der Linie suchen.

Die Fragen sollen dann die Zeilen im Speicher halten oder nicht.

Wenn die Zeile 1 k dann 300 MB Speicher.
Wenn eine Zeile 1 Megabyte ist, dann 300 GB Speicher.

Stream.Readline wird ein geringes Speicherprofil aufweisen Da es sortiert ist, können Sie aufhören zu suchen, sobald es größer als ist.

Wenn Sie es im Speicher halten, dann ein einfaches

%Vor%

Mit LINQ wird funktionieren.
LINQ ist nicht schlau genug, um die Vorteile auszunutzen, aber gegen 300K wäre immer noch ziemlich schnell.

BinarySearch wird die Sortierung nutzen.

    
paparazzo 15.01.2013 17:23
quelle
1

Ich schrieb dies schnell für die Arbeit, könnte verbessert werden auf ...

Definieren Sie die Spaltennummern:

%Vor%

Definieren Sie das Modell

%Vor%

Importieren und füllen Sie eine Liste von Modellen:

%Vor%

Gibt Ihnen eine Liste stark typisierter Objekte zurück

    
Sam Jones 25.06.2015 19:34
quelle
0

Probieren Sie den kostenlosen CSV-Reader aus. Keine Notwendigkeit, das Rad immer wieder neu zu erfinden;)

1) Wenn Sie die Ergebnisse nicht speichern müssen, wiederholen Sie einfach die CSV - behandeln Sie jede Zeile und vergessen Sie sie. Wenn Sie alle Zeilen immer wieder bearbeiten müssen, speichern Sie sie in einer Liste oder einem Wörterbuch (mit einem guten Schlüssel natürlich)

2) Versuchen Sie die generischen Erweiterungsmethoden wie diese

%Vor%     
Matthias 15.01.2013 16:55
quelle
0

Hier ist mein VB.net Code. Es ist für ein Angebot qualifizierter CSV, also für eine reguläre CSV, ändern Sie Let n = P.Split(New Char() {""","""}) zu Let n = P.Split(New Char() {","})

%Vor%     
China Syndrome 15.01.2013 17:04
quelle
0

Normalerweise würde ich empfehlen, einen dedizierten CSV-Parser zu finden (wie dies oder dies ). Allerdings habe ich diese Zeile in Ihrer Frage bemerkt:

  

Ich muss für eine gegebene Eingabe feststellen, ob irgendeine Zeile in der CSV mit dieser Eingabe beginnt.

Das sagt mir, dass die Computerzeit, die CSV-Daten analysiert, bevor dies bestimmt wird, Zeitverschwendung ist. Sie brauchen nur Code, um einfach Text für Text zu finden, und Sie können dies über einen String-Vergleich so leicht wie alles andere tun.

Zusätzlich erwähnen Sie, dass die Daten sortiert sind. Dies sollte Ihnen ermöglichen, die Geschwindigkeit enorm zu erhöhen ... aber Sie müssen wissen, dass Sie, um dies zu nutzen, Ihren eigenen Code schreiben müssen, um Suchanfragen auf Low-Level-Dateistreams zu stellen. Dies ist bei weitem Ihr bestes Ergebnis, aber es wird bei weitem erfordern die meisten anfängliche Arbeit und Wartung.

Ich empfehle einen technischen Ansatz, bei dem Sie ein Leistungsziel festlegen, etwas relativ Einfaches erstellen und die Ergebnisse mit diesem Ziel vergleichen. Beginnen Sie insbesondere mit dem 2. Link, den ich oben gepostet habe. Der CSV-Reader wird nur einen Datensatz gleichzeitig in den Speicher laden, also sollte er einigermaßen gut funktionieren, und es ist einfach, damit anzufangen. Erstellen Sie etwas, das diesen Reader verwendet, und messen Sie die Ergebnisse. Wenn sie Ihr Ziel erreichen, dann hören Sie auf.

Wenn sie Ihr Ziel nicht erreichen, passen Sie den Code so an den Link an, dass Sie beim Lesen jeder Zeile zuerst einen Zeichenfolgenvergleich durchführen (bevor Sie die CSV-Daten analysieren) und nur die Arbeit ausführen, um csv zu analysieren die Zeilen, die übereinstimmen. Dies sollte besser funktionieren, aber nur dann, wenn die erste Option nicht Ihren Zielen entspricht. Wenn dies der Fall ist, messen Sie die Leistung erneut.

Wenn Sie das Leistungsziel immer noch nicht erreichen, können Sie auch Code auf niedriger Ebene schreiben, um mit Suchaufrufen eine binäre Suche in Ihrem Dateistream durchzuführen. Dies ist wahrscheinlich das Beste, was Sie in Bezug auf die Leistung erreichen können, aber es wird sehr chaotischer und fehleranfälliger Code zum Schreiben sein, und Sie wollen nur hierhin gehen, wenn Sie Ihre Ziele aus früheren Schritten absolut nicht erreichen .

Denken Sie daran, dass die Leistung eine Funktion ist, und genau wie jedes andere Feature müssen Sie evaluieren, wie Sie dieses Feature relativ zu realen Designzielen erstellen. "So schnell wie möglich" ist kein vernünftiges Designziel. Etwas wie "Reagieren auf eine Benutzersuche innerhalb von .25 Sekunden" ist ein echtes Designziel, und wenn der einfachere, aber langsamere Code dieses Ziel immer noch erreicht, müssen Sie damit aufhören.

    
Joel Coehoorn 16.01.2013 17:32
quelle

Tags und Links