Große Datei (1,5gb) mit ca. 500.000 Zeilen schnell einlesen

  • VB.NET

Es gibt 11 Antworten in diesem Thema. Der letzte Beitrag () ist von slice.

    Große Datei (1,5gb) mit ca. 500.000 Zeilen schnell einlesen

    Hallöchen,

    ich möchte Dateien aus unserer Warenwirtschaft zur Laufzeit auslesen. (.dat) Dateien.

    Eine Datei mit ca. 500.000 Zeilen hat momentan 1,5 Gb.
    Man könnte jetzt anfangen darüber zu streiten wie bescheuert die Warenwirtschaft hier arbeitet aber da lässt sich momentan nichts dran ändern.

    Die Datei muss im Lese Modus geöffnet werden, da sie durch die Warenwirtschaft auch im Zugriff sein kann.
    ReadallLines fliegt somit raus, wenn mich nicht alles täuscht.

    Momentan mache ich das über einen Streamreader (Do Until reader.EndofStream) aber der braucht unverschämt lange. So ca. 3 Minuten.

    Gibt es da noch andere Methoden um eine Datei super schnell einzulesen? Ich muss nämlich Zeile für Zeile immer an bestimmer Position und für eine bestimmte Länge splitten.

    Z.b.

    Artikelnummer: Position 1, Länge 25
    Bestellnummer: Position 26, Länge 8

    usw.

    Danke

    *Topic verschoben*

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Marcus Gräfe“ ()

    Wo liegt denn die Datei? auf nem Netzlaufwerk? Lokal?
    Es ist halt die Frage, ob du nicht im Moment am Hardwarelimit agierst.
    Vielleicht lässt sich aber auch was am Code optimieren. Stell doch mal den Teil hier rein, der für das Auslesen und Splitten verantwortlich ist.

    Und was passiert nach dem Split? wird die Datei dann zurückgeschrieben? oder musst du "nur" die Daten auslesen?
    Also ich verarbeite für einen Kunden eine ca. 400MB "große" (2,5-3 mio Zeilen) Datei innerhalb weniger Sekunden.
    Ich öffne ein StreamReader und gehe die Datei Zeile für Zeile durch:

    C#-Quellcode

    1. StreamReader inputStream = new StreamReader(sourceFile, Encoding.UTF8, false);
    2. foreach (string line in inputStream.Lines())
    3. {
    4. // todo
    5. }
    6. // Die "Lines" Methode ist eine Extension
    7. public static IEnumerable<string> Lines(this TextReader reader)
    8. {
    9. while (reader.Peek() >= 0)
    10. {
    11. yield return reader.ReadLine();
    12. }
    13. }


    Edit: Für dein "readonly" kannst du noch ein FileStream vor den StreamReader positionieren.
    Die Frage ist halt, wie dein Code für die Verarbeitung ausschaut, der kann den Prozess extremst verlangsamen.
    Das hört sich schonmal nicht schlecht an.
    Leider gibt es in VB ja kein yield deshalb wird es zumindest für mich schwer, das ganze in VB zu übersetzen.

    Hatte gerade zwar schon kurz "eine Art ergebnis" innerhalb von 2 Sekunden aber da standen nur zahlen die nicht aus der Datei kommen konnten :P

    Danke!

    //Edit habe es selber hinbekommen. Iterator war das Stichwort.
    Mal schauen wie die Performance ist, wenn ich noch ein wenig mehr einbaue ;)

    Vielen Dank!

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Holistiker“ ()

    Du brauchst das yield nicht unbedingt, das ganze war ja nur eine Extension die es ermöglichte alle Zeilen wie bei File.ReadAllLines() mittels For-Loop zu durchlaufen.

    C#-Quellcode

    1. using (var stream = new FileStream(@"D:\Desktop\test2.txt", FileMode.Open, FileAccess.Read))
    2. using (var reader = new StreamReader(stream, Encoding.UTF8, false))
    3. {
    4. while(reader.Peek() >= 0)
    5. {
    6. var line = reader.ReadLine();
    7. }
    8. }

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „seh“ ()

    Also für mich scheint das aber ein signifikanter unterschied zu schein (Performance technisch) ob ich mit IEnumerable arbeite oder nicht.

    Mit dem Code von Slice braucht das Tool für das Auslesen aller Zeilen inklusive direktem Schreiben in die neue Datei gerade einmal 4 Sekunden.

    Alle anderen Methoden (und das waren einige) haben gefühlte Ewigkeiten gebraucht.
    22

    Bevor ich jetzt ein neues Thema aufmache:
    Weiss jemand warum ich trotzdem ein "Datei ist bereits durch ein anderes Programm in Bearbeitung" bekomme?

    VB.NET-Quellcode

    1. Dim st As New System.IO.FileStream(Dateiname, IO.FileMode.Open, FileAccess.Read, FileShare.Read)
    2. Dim reader As StreamReader = New StreamReader(st, System.Text.Encoding.UTF8, False)


    Habe ich da was vergessen?

    //FileShare.Readwrite sollte es natürlich heißen. Jetzt läuft es auch. *autsch*

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Holistiker“ ()

    Kann es sein, dass das Programm, welches in die Datei reinschreibt keinen Zugriff (auch nicht Lesezugriff) von anderen Prozessen erlaubt? Das ist nämlich durchaus möglich.
    Versuch mal FileShare.ReadWrite.

    Das FileShare ist nicht für den eigenen Prozess gedacht sondern wird benötigt um zu wissen welche Rechte das File-Handle des anderen Prozesses hat. Die müssen glaub ich übereinstimmen.

    Wenn es mit FileShare.ReadWrite funktioniert, hast du (so viel ich weiß) sogar Schreibrechte in die Datei wenn FileAccess ReadWrite ist.

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „seh“ ()

    Ja mit ReadWrite hat es funktioniert. Aber ich fang besser garnicht erst an direkt in die "Datenbankdateien" unserer Warenwirtschaft zu schreiben :D

    Kann ich bei der der Methode von slice irgendwie die erste Zeile überspringen? Da steht nämlich irgendein Index oder sowas drin, den ich zum Auslesen nicht brauche.
    Ich könnte das natürlich irgendwie anders lösen aber die erste Zeile zu überspringen, würde das ganze ein wenig eleganter machen.
    @Holistiker @seh hat aber schon recht, ob yield oder nicht macht keinen wirklichen Unterschied (eher sogar umgekehrt), die Frage ist mit was du es vergleichst.
    Allgemein sei aber noch anzumerken, das es einen unterschied in der Performance macht, wo die Daten liegen (HDD vs. SSD).

    C#-Quellcode

    1. string filePath = @"D:\test.csv"; // 10147528 Lines (1,47 GB)
    2. List<string> data = new List<string>();
    3. Stopwatch stopwatch = new Stopwatch();
    4. using (StreamReader inputStream = new StreamReader(filePath, Encoding.UTF8, false))
    5. {
    6. stopwatch.Start();
    7. foreach (string line in inputStream.Lines())
    8. {
    9. data.Add(line.Substring(0, 5));
    10. }
    11. stopwatch.Stop();
    12. Console.WriteLine(" Yield: {0}", stopwatch.Elapsed);
    13. }
    14. data.Clear();
    15. stopwatch.Reset();
    16. using (StreamReader inputStream = new StreamReader(filePath, Encoding.UTF8, false))
    17. {
    18. stopwatch.Start();
    19. while (inputStream.Peek() >= 0)
    20. {
    21. string line = inputStream.ReadLine();
    22. data.Add(line.Substring(0, 5));
    23. }
    24. stopwatch.Stop();
    25. Console.WriteLine("No Yield: {0}", stopwatch.Elapsed);
    26. }


    Quellcode

    1. Yield: 00:00:06.6479317
    2. No Yield: 00:00:06.3029913


    Edit:
    Klar, einfach vor der Schleife einmal "reader.ReadLine()" bzw. "Lines()" aufrufen :D