CSV einlesen mit Werteparsing

  • Es gibt zwar schon ein paar Funktionen zum Einlesen von CSV aber manche kommen mit Feinheiten wie escaped quotes oder Zeilenumbrüche in einem Value nicht klar.

    Außerdem habe ich mir eine Funktion gewünscht, welche gleich das benutzerdefinierte Parsing der Werte übernimmt (z. B. "deutsche" Zahlen mit "," als Dezimaltrennzeichen sollen gleich als AutoIt-Zahlentyp eingelesen werden oder sowas).

    Des Weiteren hätte ich gerne (wie z.B. bei Python) die Werte nicht nur als 2D-Array sondern die einzelnen Datensätze gleich als Objekte vorliegen. (macht es bei der Verarbeitung hinterher einfacher wenn man einfach den Attributnamen des Objektes angeben kann anstatt dass man mit Indizes arbeiten muss).

    Daher habe ich halt selbst eine entsprechende Funktion geschrieben und könnte mir vorstellen, dass noch jemand Verwendung für diese finden könnte.

    Die _parseCSV ist für das Einlesen und konvertieren in ein Array zuständig und kann folgendes:

    • Einlesen von CSV-Dateien oder Strings
    • kommt mit escaped Quotes und Zeilenumbrüchen in Values klar
    • Beliebiges Austauschen der Trennzeichen und Quote-Zeichen
    • Headerzeile ignorieren ist möglich
    • Für jede Spalte kann eine Funktion angegeben werden mit der die jeweiligen Werte konvertiert / geparsed werden sollen

    Die _Array2Dics konvertiert ein 2D-Array (wie z. B. von der _parseCSV zurückgegeben) in ein Array mit Objekten.
    Die Attributnamen kommen entweder aus der ersten Zeile (Headerzeile) oder können benutzerdefiniert übergeben werden:

    Zu guter letzt macht es in dem Zusammenhang noch Sinn eine Funktion zu haben, welche aus einem 2D-Array eine CSV bastelt:

    AutoIt
    #include "parseCSV.au3"
    
    ; 2D-Array erzeugen:
    Global $s_CSV = BinaryToString(InetRead('https://pastebin.com/raw/3N1xVAN0'), 4)
    Global $aData = _parseCSV($s_CSV, True, False, ';', '"', ';__PunktWeg;__ZahlDeutsch;__PunktWeg;Number')
    
    ; schreibe csv-Datei
    FileWrite("out.csv", _Array2CSV($aData, Default, ','))

    Und ebenfalls liegt noch die Funktion _StringHandleTable() dabei.
    Die ist ähnlich wie die parseCSV - jedoch für Tabellen wo die Spalten jeweils eine feste Breite haben (wie z.B. bei Ausgaben von printf) und eben nicht über ein Spaltentrennzeichen abgetrennt werden.
    Das Parsing von Konsolenausgaben dürfte sicher der häufigste Anwendungsfall für diese Funktion sein:

    Changelog:

    2021/02/10
    • Code entschlackt
    • geringe Performanceverbesserungen (ca. 10% in meinen Messungen)
    2020/07/22
    • Code entschlackt
    • geringe Performanceverbesserungen
    2020/07/21
    • Performance deutlich gesteigert (durch Vereinfachung der internen RegEx-Pattern)
    2020/06/16
    • initiale Version
  • Sehr gut! :thumbup:

    In beiden _Array2* Beispielen fehlt ein #include "_parseCSV.au3" und FileWrite ohne das Drumherum ist nicht Optimum, da immer in die bestehende Datei angehängt wird und Unicode ist aus nicht, zumindest auf meiner Kiste. :)

    AutoIt
    #include <FileConstants.au3>
    ; schreibe csv-Datei
    Global $hFile = FileOpen("out.csv", BitOR($FO_OVERWRITE, $FO_UTF8))
    FileWrite($hFile, _Array2CSV($aData))
    FileClose($hFile)

    Auch am Arsch geht ein Weg vorbei...

    ¯\_(ツ)_/¯

  • In beiden _Array2* Beispielen fehlt ein #include "_parseCSV.au3"

    Stimmt - War jetzt nicht als ausgewachsene UDF gedacht sondern nur als 3 eigenständige kleine Funktionen.

    Daher nur die Beispiele wo ich davon ausging, da fügt sich einer noch die Funktion jeweils dazu.

    Aber gut - ich werde das mal als UDF verpacken und oben so dran hängen.
    Dann wäre eine konsistente Namensgebung der Funktionen aber sinnvoll - Vorschläge? Gerade im Hinblick auf eventuelle weitere Funktionen die da thematisch reinpassen würden?

    und FileWrite ohne das Drumherum ist nicht Optimum, da immer in die bestehende Datei angehängt wird

    Ja stimmt - wollte das Beispiel jedoch nicht groß aufblähen.

    Sollte ja suggerieren das man damit alles gaaaaanz easy in einer Zeile abfrühstücken kann :D

  • Das wäre der Name der UDF-Datei. Mir ging es jedoch um die Funktionen selber.
    Also wie z.B. die Array-Funktionen in der Array.au3 - die fangen alle mit _Array... an und zeigen damit schon am Namen wo sie hingehören.

    Die 3 Funktionen hier sind jedoch vom Namen noch inkosistent.

  • Ich benenne die Funktionen immer andersrum. Zuerst die Funktion, dann die Aktion. So stehen Funktionen für das selbe Objekt alphabetisch beieinander. So machen die AutoIt UDFs das ja auch (hoffentlich).

    Also: _CSVParse, _CSVRead, _CSVWrite ...

  • Jaja sowas hätte ich ja auch gemacht - das Problem ist bloß dass die aktuelle _Array2Dics im Grunde gar nix mit CSV zu tun hat.
    Wenn ich die irgendwie zusammenhauen soll brauche ich sowas wie einen gemeinsamen thematischen Nenner - ich sehe halt hier bisher keinen.

  • Ich würde bei _Array2Dics bleiben (vielleicht auf _Array2Dicts anpassen - die Amis/Britten verstehen das sonst vielleicht falsch ;) )

    Denn das Hauptobjekt ist ja ein Array (Array rein, Array raus).

  • Ja wahrscheinlich das beste.

    Ne ausgewachsene UDF stellen die 3 Funktionen ja eh nicht dar - nur ein Ort wo sie gemeinsam abgespeichert sind.

    vielleicht auf _Array2Dicts anpassen - die Amis/Britten verstehen das sonst vielleicht falsch ;)

    Ich lass es mal so - vielleicht fördert das ja deren Verbreitung ;)