optimierte/schnelle CSV-Parser



  • Hallo,
    ich habe folgende Problemstellung: Es gibt mehrere hundert Dateien in denen sich eine bestimmte (für alle Dateien feste) Anzahl an Werten befindet, welche durch Komma oder Semikolon voneinander getrennt sind. Ich möchte diese Werte nach einander einlesen und parsen.

    Da ich meine, dass C++ so eine Funktion nicht besitzt und auch dementsprechendes nicht im Internet oder meinen Büchern gefunden habe, habe ich mir erst mal selbst eine Funktion geschrieben:

    void Parse(const string& str, vector<string>& tokens)
    {
       char  *token;
       char *buffer = new char[str.length()+1];
       memcpy(buffer, str.c_str(), str.length() + 1);
    
       token = strtok(buffer, ",");
    
        while( token != NULL )
        {
           tokens.push_back(token);
           token = strtok(NULL, ","); 
        }
    }
    

    Das ist jetzt nichts wildes, aber irgendwie für meinen Geschmack viel zu langsam. Ich hab leider keine genaue Vorstellung davon, wie schnell man mein Problem lösen kann, aber bei dem obrigen Code brauche ich ca. 50 sec um 500 Dateien a 100 000 Werten zu parsen.

    Nach einigem Suchen in diversen Foren (auch hier ^^) habe ich eine andere Methode gefunden:

    string::size_type lastPos = str.find_first_not_of(",", 0);
        string::size_type pos     = str.find_first_of(",", lastPos);
    
        while (string::npos != pos || string::npos != lastPos)
        {
            tokens.push_back(str.substr(lastPos, pos - lastPos));
            lastPos = str.find_first_not_of(",", pos);
            pos = str.find_first_of(",", lastPos);
        }
    

    Das dauert aber ungefähr genauso lange.

    Meine Frage nun: Kennt jemand eine Möglichkeit das ganze schneller zu lösen oder kann mir ein paar Stichpunkte etc nennen, sodass ich selbst auf die Lösung komme, oder ist es nicht möglich? Wobei ich halt ne knappe Minute schon als sehr lang ansehe ;).

    Vielen Dank
    Cabo



  • um 500 Dateien a 100 000 Werten zu parsen.

    Heißt das 50 Mio string Objekte?


  • Mod

    Geschwindigkeit praktisch jeden CSV-Parsers >> Geschwindigkeit von Datenträgern

    Deshalb wird dir eine Optimierung nichts bringen und es kommt immer nur die Geschwindigkeit deiner Festplatte raus.



  • zur Zeit habe ich das als string, ja... ich bin aber auch für andere Formate oder Möglichkeiten offen



  • Liest du die Dateien zeilenweise ?

    Es könnte effizienter sein größere Blöcke zu lesen.



  • hallo,
    vielen dank für eure posts,
    scheinbar habe ich SeppJs antwort gerade übersehen ^^ sry.
    an meiner festplatte sollte es nicht liegen. ich hab ein gleiches programm mit matlab geschrieben und damit funktioniert es WESENTLICH schneller.

    das einlesen der dateien geschieht bereits in größeren blöcken und ist für meine ansprüche auch schnell genug .da reine einlesen und speichern in string objekten dauert für alle 500 Dateien zusammen ca 3 Sekunden. Ob das Programm also 1 Minute oder 1 Minute und 3 Sek braucht ist mir egal und kann ruhig vernachlässigt werden.

    könnte man vielleicht wirklich auf ein anderes format umsteigen? zum beispiel reine binärdaten? die werte befinden sich alle zwischen -127 und 127 und sind somit gut mit 8 Bit darstellbar.



  • Bist du denn sicher, dass Matlab alle Tokens beim einlesen generiert oder erst beim Lesen. Denn allein das Füllen eines vectors mit 50mio strings dauert schon ein Weilchen. Du kannst dir z.B. mal boost tokenizer angucken. Der gibt auch immer nur das nächste token zurück wenn du danach fragst. Ich glaube der hat auch ein fertiges csv typedef.



  • also ich schreibe nicht alle Werte in einen einzigen string. das komplette Programm sieht so aus, dass ich eine Datei einlese, die Daten parse und verarbeite und danach die nächste Datei einlese und die Daten parse etc.
    Dazu habe ich eine einfache for-Schleife benutzt und für Testzwecke die ganzen Verarbeitungsschritte auskommentiert. Ich brauche also für einen Durchlauf nur 100.000 Werte die irgendwie bereit stehen müssen und sofort bearbeitet werden. Ist es hier auch sinnvoll jeden Wert einzeln anzufordern?

    Ich werde mir jetzt aber auf jeden Fall mal dieses boost Tokenizer anschauen. Danke für den Tip.



  • Hallo Cabo,

    ich unterstelle mal, Du weißt welches Format Deine CSV-Datei hat. Dann versuche doch mal, die Variablen gleich mit getline( file, token, ',' ) einzulesen, statt die Zeilen zu lesen und anschließend die Zeilen zu zerlegen. Das sollte bereits um einiges schneller sein.
    Wenn alles Zahlen (z.B. Integer) sind, die in der CSV-Datei stehen, so kannst Du auch gleich int-varibalen einlesen; angenommen da sind 4 int pro Zeile mit Komma getrennt, so liest Du diese mit

    char k;
        int i1, i2, i3, i4;
        file >> i1 >> k >> i2 >> k >> i3 >> k >> i4;
    

    Gruß
    Werner



  • rufe zuerst ein vector.reserve(100000) auf, damit der Vektor mittendrin nicht anfangen muss, alle Objekte neu zu kopieren (bei Strings ist das durchaus teuer!). Benutze alternativ ein deque.



  • vielen dank schon mal für eure weiteren Tips. ich werde alle ausprobieren und hoffe, dass ich mein Programm so verbesser kann. sobald ich ein zufriedenstellendes Ergebnis habe, sage ich euch, wie ich es gelöst habe.
    Derweil sind weitere Vorschläge natürlich Willkommen.
    Grüße
    Cabo


Anmelden zum Antworten