1. Dashboard
  2. Mitglieder
    1. Letzte Aktivitäten
    2. Benutzer online
    3. Team
    4. Mitgliedersuche
  3. Forenregeln
  4. Forum
    1. Unerledigte Themen
  • Anmelden
  • Registrieren
  • Suche
Alles
  • Alles
  • Artikel
  • Seiten
  • Forum
  • Erweiterte Suche
  1. AutoIt.de - Das deutschsprachige Forum.
  2. Mitglieder
  3. hausl78

Beiträge von hausl78

  • CSV mit FileReadLine() - Alternative wenn Umbruch innerhalb Quotes

    • hausl78
    • 1. Dezember 2016 um 15:48

    Hallo,

    danke, aber an jedem Zeilenende hängt ein CR|LF|CRLF. Da definiiert ja ein Zeilenende. Ich habe jetzt aber zwei Thread gefunden mit CSV Parsern die scheinbar damit umgehen können. Das schau ich mir an.

    https://www.autoitscript.com/forum/topic/38397-csv-udf/

    https://www.autoitscript.com/forum/topic/50…ick-csv-parser/


    AutoIt
    Func SplitCsvString($sString, $sDelim)
    
    
        ; alle Delimiter ausserhalb von Anführungszeichen in Chr(1) wandeln und danach splitten
        Local $sReplaceChr = Chr(1)
        Local $sPattern = $sDelim & '(?=(?:[^"]*"[^"]*")*[^"]*$)'
        Local $sTemp = StringRegExpReplace($sString, $sPattern, $sReplaceChr, 0)
        Local $aSplit = StringSplit($sTemp, $sReplaceChr, 1)
    
    
        Return $aSplit
    EndFunc
    Alles anzeigen

    Weiters nutze ich bisher diese Funktion für die Feldtrennung, die geschütze Semikolons innerhalb " " ignoriert .DIe habe ich nun auch aufgebohrt, scheint für Zeilenumbrüche soweit auch gut zu funktionieren. Auf den ersten Blick.

  • CSV mit FileReadLine() - Alternative wenn Umbruch innerhalb Quotes

    • hausl78
    • 30. November 2016 um 15:32

    Hallo,

    ich verarbeite derzeit CSV Dateien mit AutoIt. Nun habe ich vermehrt den Fall, das in einem Feld mit " " gekapselte Werte kommen die Zeilenumbruche enthalten. Leider lese ich es derzeit immer mit FileReadLine() ein, was natürlich zu Problemen führt.

    Beispiel:

    Code
    eins;zwei;drei;vier
    eins;zwei;"drei 
    und
    vier";fünf
    eins;zwei;drei;vier


    Wie kann ich da am besten vorgehen? Prüfen wie viele " in der Datei sind und wenn es ungerade sind die nächste Zeile von FileReadLine() einfach vor meiner Verarbeitung dranhängen? Scheint mir auch etwas nach Workaround.. Was anderes fällt mir gerade nicht ein.

    Gibt es hier ev. gute CSV-Parser die das können? (Semikolons innerhalb " " kann ich schon handeln, aber das passiert ja eh erst danach).

    Danke!

  • CSV (Zeilen) parsen mit korrektem Handling von escaping

    • hausl78
    • 27. Mai 2014 um 08:55

    Ich habe nun noch im www diesen Thread gefunden, dort wird auch ein Pattern/Variante angeführt, die gut zu funktionieren scheint.. Hier mal die von mir angepasste (für meine Zwecke) fertige Funktion - der Vollständigkeit halber, oder falls jemanden etwas "blödes" dabei auffält, bitte danke für jeden Input dazu.
    http://www.autoitscript.com/forum/topic/10…ted-delimiters/


    [autoit][/autoit][autoit][/autoit][autoit]

    #include <Array.au3>

    [/autoit][autoit][/autoit][autoit]

    local $sCSV = 'Hans;im;"Glück";"Und;so;weiter;immer;heiter";und;weiter'

    [/autoit][autoit][/autoit][autoit]

    local $aSplitData = SplitCsvString($sCSV, ";")
    _ArrayDisplay($aSplitData)

    [/autoit][autoit][/autoit][autoit]

    #cs
    Row|Col 0
    [0]|6
    [1]|Hans
    [2]|im
    [3]|Glück
    [4]|Und;so;weiter;immer;heiter
    [5]|und
    [6]|weiter
    #ce

    [/autoit][autoit][/autoit][autoit][/autoit][autoit]

    Func SplitCsvString($sString, $sDelim)

    [/autoit][autoit][/autoit][autoit]

    Local $sReplaceChr = Chr(1)
    ;Local $sReplaceChr = "#"
    Local $sPattern = $sDelim & '(?=(?:[^"]*"[^"]*")*(?![^"]*"))'
    Local $sTemp = StringRegExpReplace($sString, $sPattern, $sReplaceChr, 0)
    ;Consolewrite($sTemp & @CRLF)

    [/autoit][autoit][/autoit][autoit]

    ;Strip trailing character if it matches the reserved pattern
    ;If StringRight($sTemp, 1) = $sReplaceChr Then $sTemp = StringTrimRight($sTemp, 1)

    [/autoit][autoit][/autoit][autoit]

    ;Split string using entire reserved string
    Local $aSplit = StringSplit($sTemp, $sReplaceChr, 1)

    [/autoit][autoit][/autoit][autoit]

    ; Anführungszeichen vorne und hinten entfernen, wenn vorhanden
    For $i = 1 To $aSplit[0]
    If StringRight($aSplit[$i], 1) = '"' Then $aSplit[$i] = StringTrimRight($aSplit[$i], 1)
    If StringLeft($aSplit[$i], 1) = '"' Then $aSplit[$i] = StringTrimLeft($aSplit[$i], 1)
    Next

    [/autoit][autoit][/autoit][autoit]

    Return $aSplit
    EndFunc

    [/autoit]

    LG

  • CSV (Zeilen) parsen mit korrektem Handling von escaping

    • hausl78
    • 27. Mai 2014 um 01:33

    Frage noch dazu.. wie würde das Pattern von der ersten Variante aussehen, wenn auch mehrere Semikolon innerhalb der " " erlaubt sind?

    [autoit]

    local $sReplaced = StringRegExpReplace($str, '([^"]*"[^;]*)(;)([^;]*"[^"]*)', '$1' & $sEscChr & '$3')

    [/autoit]

    Also zB in

    Code
    Hans;trinkt;gerne;"Cola;Bier;Wein;Schnaps"

    Ist das dann mit Regex noch machbar? Sonst bau ich das Ding um...

  • CSV (Zeilen) parsen mit korrektem Handling von escaping

    • hausl78
    • 28. April 2014 um 22:34
    Zitat

    ..schreib es rein :whistling:

    Klaro, hab ich doch, sonst hätte ja "mein" AutoIt gestreikt ;)

    Zitat

    das muß ich mal anschauen

    Danke, keinen Stress, ich fand die vorige Funktion auch schon sehr hilfreich :)

  • CSV (Zeilen) parsen mit korrektem Handling von escaping

    • hausl78
    • 28. April 2014 um 22:09

    Jetzt aber wirds kompliziert ;)

    - in der Zeile fehlte das local vorher

    [autoit]

    Local $aMatch = StringRegExp($_sLine, $sPattern, 3)

    [/autoit]

    Die Funktion hat Probleme mit leeren Feldern, es werden nur Felder mit Wert übernommen

    [autoit]


    #include <Array.au3>

    [/autoit][autoit][/autoit][autoit]

    $string = 'Peter;Paul;Stan;;;Ollie'

    [/autoit][autoit][/autoit][autoit]

    $aSplit = _CSV_SplitLine($string)

    [/autoit][autoit][/autoit][autoit]

    _ArrayDisplay($aSplit)

    [/autoit]

    gibt

    Code
    Row|Col 0
    [0]|4
    [1]|Peter
    [2]|Paul
    [3]|Stan
    [4]|Ollie

    Sollte aber vor Ollie noch zwei leere Felder ausgeben

    Ev. war das der Grund für den anderen Regex?

  • CSV (Zeilen) parsen mit korrektem Handling von escaping

    • hausl78
    • 28. April 2014 um 19:28
    [autoit]


    #include <Array.au3>

    [/autoit][autoit][/autoit][autoit]

    $string = 'Peter;"Paul; the Best";Mary;Coca-Cola;"Beer; dark";Lemon'

    [/autoit][autoit][/autoit][autoit]

    $aSplit = StringRegExp($string, '"[^;]*;[^;]*"|[^;]+', 3)

    [/autoit][autoit][/autoit][autoit]

    _ArrayDisplay($aSplit)

    [/autoit]

    Jetzt hab ich zu deiner letzten Variante (Pattern) doch noch zwei Dinge:

    1. Im ersten [0] ArrayElement gibt es die Anzahl nicht, was ich gesehen habe kann das StringRegExp() nicht, die Anzahl der matches angeben

    2. Bei den eingeschlossenen sind die " .... " noch enthalten, die müsst ich noch mit trim() oder mit captures wegmachen, wenns ist.

    Code
    Row|Col 0
    [0] | Peter
    [1] | "Paul; the Best"
    [2] | Mary
    [3] | Coca-Cola
    [4] | "Beer; dark"
    [5] | Lemon
  • Einfaches Programm zum Zeilen Löschen

    • hausl78
    • 28. April 2014 um 13:12

    Hi,

    Vorschlag: Die Ursprungsdatei mit FileReadLine zeilenweise abarbeiten und jede x-te Zeile - je nach Wunsch kannst du immer mit einer Modulo Division ( http://translation.autoit.de/onlinehilfe/functions/Mod.htm ) machen - in eine zweite neue Datei schreiben.

    Das ist gleich geschrieben, bin jedoch gerade in der Arbeit, da geht "leider" gar nicht.s.

  • CSV (Zeilen) parsen mit korrektem Handling von escaping

    • hausl78
    • 28. April 2014 um 12:15

    Hey sehr cool, danke! Ich habe die alte Func von dir etwas umgebaut, weil ich ja ein Pendat zu StringSplit() brauche, das mir ein Array zurückgibt.

    [autoit]

    func SplitCsvString($str, $sDelim)

    [/autoit][autoit][/autoit][autoit]

    local $sEscChr = Chr(1)
    local $sReplaced = StringRegExpReplace($str, '([^"]*"[^;]*)(;)([^;]*"[^"]*)', '$1' & $sEscChr & '$3')
    local $aSplit = StringSplit($sReplaced, $sDelim)

    [/autoit][autoit][/autoit][autoit]

    If StringInStr('\.^$|[({*+?#', $sEscChr) Then
    $sEscChr = '\' & $sEscChr ; mask meta characters
    EndIf

    [/autoit][autoit][/autoit][autoit]

    For $i = 1 To $aSplit[0]
    $aSplit[$i] = StringRegExpReplace($aSplit[$i], '"([^' & $sEscChr & ']*)(' & $sEscChr & ')([^' & $sEscChr & ']*)"', '$1;$3')
    Next

    [/autoit][autoit][/autoit][autoit]

    return $aSplit
    EndFunc

    [/autoit]

    Werde aber das relevante von oben noch reinpacken, super.

    Jetzt würde nur noch das Doppelte Anführungzeichen fehlen um auch Felder mit Anfürhungszeichen zu escapen dann wäre es schon die komplette cvs Spezifikation :D (Scherz).

    Danke nochmals!
    LG

  • CSV (Zeilen) parsen mit korrektem Handling von escaping

    • hausl78
    • 27. April 2014 um 23:12
    Zitat

    Ein .* würde jedes beliebige Zeichen, also auch das Gänsefüßchen matchen

    Ah, das war was ich nicht bedacht hatte.. Regex... immer wieder ..

    Danke!

  • CSV (Zeilen) parsen mit korrektem Handling von escaping

    • hausl78
    • 27. April 2014 um 21:22

    Danke für die genaue Darstellung, sehr nett! Da war ich im Grunde mit dem Verstehen gar nicht so weit weg, ich frage mich nur - nicht falsch verstehen es funktioniert ja gut - warum der Ansatz so ist, oder besser ich verstehe es nicht ;)

    Mein Regex Ansatz wäre gewesen:

    Suche ein " ... ; ... " und ersetze das ; druch in dem Fall ein chr(1). Und dann hinten raus nach dem StringSplit() das chr(1) wieder retour in ein ;

    Also im Grunde sowas ganz grob jetzt - ich bin nur Grundkenntnis-Regexer (ohne caputre Gruppen mal):

    Code
    ;".*;.*"

    Im ersten Feld des Satzes gibt es de facto kein hochkomma, daher die mal ausser acht gelassen und mit ; begonnen.

    Du suchst ja kein " etc... Weißt was ich meine?

    LG

  • Codeing Practise

    • hausl78
    • 27. April 2014 um 21:08

    Ok, danke ;)

  • Codeing Practise

    • hausl78
    • 27. April 2014 um 20:51

    Hallo!

    Geht darum - und die Empfehlung wie man noch in der Größe unbekannte Arrays deklariert, stimmt das so?

    http://www.autoitscript.com/wiki/Best_coding_practices

    [autoit]


    ; Assign a Local variable an array of numbers.
    Local $aiArray = 0

    [/autoit]

    Da komm ich dann weder durch "überschreiben" noch mit ReDim ran, daher mache ich es immer mit $a[0].

    Was hab ich übersehen?

    DAnke!

  • CSV (Zeilen) parsen mit korrektem Handling von escaping

    • hausl78
    • 27. April 2014 um 20:12

    Habe gerade mit beiden Funktionen getestet, der unterschied ist minimal, bei einer csv mit 5000 zeilen, glaub ich lasses vorerst generell mal so.


    Code
    ([^"]*"[^;]*)(;)([^;]*"[^"]*)

    Kannst du mir den noch erklären?

    Code
    ([^"]*"[^;]*) 
    ein beliebiges zeichen ausser " beliebig oft, dann ein " 
    und dann wider ein belibeiges zeichen beliebig oft ausser ; 
    
    
    dann ; 
    
    
    ([^;]*"[^"]*) 
    wie oben nur andersrum
  • CSV (Zeilen) parsen mit korrektem Handling von escaping

    • hausl78
    • 27. April 2014 um 18:59

    Hm, stimmt hast recht.. dann bleib ich glieich bei dem chr(1) .. eine Pipe | hat man ja schnell mal wo als Textzeichen, daher wollt ich eine möglichst "unübliche" erstellen.

    Dann muss ich jetzt nur noch benchmarken :) ob ich diese Funktion generell splitten lassen oder eben nur wenn ein ;" "; vorkommt und sonst die normale StringSplit() ... mal schauen.

    Danke dir jedenfalls!

  • CSV (Zeilen) parsen mit korrektem Handling von escaping

    • hausl78
    • 27. April 2014 um 17:01

    Hallo!

    Super für den Ansatz, danke. An sowas hatte ich zuerst auch gedacht, aber dann wieder verworfen weil ich das Pattern nich wirklich hingebracht habe und mir dann eingeredet Zeichenweise Verarbeitung ist sicher auch performanter ;)


    EDIT:
    Wenn ich eine Pipe drinnen habe, dann hab ich wieder das Problem, wenn ich zB ein mehrzeichen-Temp-Delim mache, dann muss ich vermulitch das Regex unten anpassen da dies den Delim in einer Zeichenklasse arbeitet [ ] was ich gesehen habe.

    [autoit]

    $sOut &= StringRegExpReplace($aSplit[$i], '"([^' & $escDelim & ']*)(' & $escDelim & ')([^' & $escDelim & ']*)"', '$1;$3') & @LF

    [/autoit]

    Wenn ich zB "~#-" als Trenner statt der Pipe verwenden möchte, oder !#! etc..

  • CSV (Zeilen) parsen mit korrektem Handling von escaping

    • hausl78
    • 27. April 2014 um 13:39

    Hallo!

    Gibt es einen best-practise-way um beim Auslesen von CSV Dateien auch mit Escaping richtig zu behandeln? Zumindest die erste Variante mit "nested quotes" sollte drinnen sein. Hab dazu für AutoIt nicht wirklich was gefunden.

    Code
    Peter;"Paul; the Best";Mary

    Also das im Prinzip generell innerhalb von " ... " jegliche Trenner ignoriert werden. Leider gibt es was ich gesehen habe dazu in StringSplit keinen optionalen Parameter.

    Hätte sonst den Gedankenansatz die Zeile Zeichen für Zeichen durchzugehen und dann diese halt so zu verarbeiten an Stelle vom derzeitigen StringSplit($line, ";")

    Gibt es hier schon Schnipsel zu dem Thema?

    Danke!

  • Ohne global in diesem Fall

    • hausl78
    • 22. April 2014 um 12:48

    Bei mir sieht es gekürzt ca. so aus...

    [autoit]


    Func frmMain_load()

    global $hBtnOpen = GUICtrlCreateButton("Datei öffnen", ...)
    global $hBtnReCheck = GUICtrlCreateButton("Datei erneut prüfen", ... )
    ; Event Listener
    While 1
    Switch GUIGetMsg()

    [/autoit][autoit][/autoit][autoit]

    ; Button "Datei öffnen"
    Case $hBtnOpen
    openFileDialog()
    ; Button "Datei erneut prüfen"
    Case $hBtnReCheck
    btn_recheck_click()

    [/autoit][autoit][/autoit][autoit]

    EndSwitch
    WEnd
    EndFunc

    [/autoit][autoit][/autoit][autoit]

    ; Dateiname ermitteln und Validierung starten
    Func openFileDialog()
    local $oldPath = $globFullFilePath ; Pfad backup
    $globFullFilePath = FileOpenDialog('Datei öffnen', @DesktopDir & "", "Text (*.csv; *.txt)|All (*.*)", $FD_FILEMUSTEXIST, '', $hFrmMain)
    If @error Then
    return False
    EndIf
    ; Pfad ok, dann Datei prüfen
    validateFile()
    EndFunc

    [/autoit][autoit][/autoit][autoit]

    ; Button "Datei erneut prüfen"
    Func btn_recheck_click()
    validateFile()
    EndFunc

    [/autoit][autoit][/autoit][autoit]

    Func validateFile()
    ; hier ne Menge Validierungszeugs....
    EndFunc

    [/autoit]

    Ich glaube ich verstehe wie ich den Pfad dann "wieder" ins frmMain zurückbekommen würde, aber wenn ich mir das durchdenke, dann werd ich es vorerst wirklich global lassen, sehe in der ständigen Durchreicherei keinen Vorteil - derzeit.

  • Ohne global in diesem Fall

    • hausl78
    • 22. April 2014 um 11:41

    Hallo!

    Danke mal für die Infos, dann werd ich es vorerst mal so belassen, es stört mich so gesehen eh nicht, ich war nur neugrierg und will eben sofern einfach möglich nicht alles globalisieren.

    Der Vollständigkeit halber, das wäre der Teil der das Form baut und die Listener-Endlosschleife.


    [autoit][/autoit][autoit][/autoit][autoit]

    ; ...
    ; ~ option explicit
    AutoItSetOption('MustDeclareVars', 1)
    ; ...
    ; div
    global $globFullFilePath = "" ; global, weiß nicht wie ich den sonst in btn_recheck_click() reinbekomme
    ; ...
    ; Hauptfenster erstellen
    Func frmMain_load()
    ; Fenster
    global $hFrmMain = GUICreate($APP_TITLE, 1100, 620, -1, 50, $WS_SIZEBOX + $WS_MAXIMIZEBOX + $WS_MINIMIZEBOX, $WS_EX_ACCEPTFILES)
    ; Text/Editbox
    global $hTextBox = GUICtrlCreateEdit('', 15, 15, 1068, 520, $WS_HSCROLL + $WS_VSCROLL + $ES_READONLY)
    GUICtrlSetResizing($hTextBox, $GUI_DOCKBORDERS) ; Resize-Optionen - fix an den Rahmen ausrichten
    GUICtrlSetFont($hTextBox, 9, 400, 0, "Courier New") ; Schriftart Textbox
    GuiCtrlSetState(-1, $GUI_DROPACCEPTED) ; Drag/Drop erlauben
    ; Buttons
    local $valign = 552
    local $height = 28
    global $hBtnOpen = GUICtrlCreateButton("Datei öffnen", 480, $valign, 120, $height)
    GUICtrlSetResizing($hBtnOpen, $GUI_DOCKBOTTOM + $GUI_DOCKRIGHT + $GUI_DOCKSIZE)
    GUICtrlSetState($hBtnOpen, $GUI_DEFBUTTON) ; Default-Button
    global $hBtnReCheck = GUICtrlCreateButton("Datei erneut prüfen", 615, $valign, 120, $height)
    GUICtrlSetResizing($hBtnReCheck, $GUI_DOCKBOTTOM + $GUI_DOCKRIGHT + $GUI_DOCKSIZE)
    GUICtrlSetState($hBtnReCheck, $GUI_DISABLE) ; Initial deaktiviert
    global $hBtnClipBoard = GUICtrlCreateButton("Inhalt in die Zwischenablage kopieren", 750, $valign, 210, $height)
    GUICtrlSetResizing($hBtnClipBoard, $GUI_DOCKBOTTOM + $GUI_DOCKRIGHT + $GUI_DOCKSIZE)
    global $hBtnClose = GUICtrlCreateButton("Beenden", 974, $valign, 110, $height)
    GUICtrlSetResizing($hBtnClose, $GUI_DOCKBOTTOM + $GUI_DOCKRIGHT + $GUI_DOCKSIZE)
    ; Funktion MY_WM_GETMINMAXINFO überwacht Mindestgröße des Fensters
    GUIRegisterMsg($WM_GETMINMAXINFO, "MY_WM_GETMINMAXINFO")
    ; GUI darstellen
    GUISetState(@SW_SHOW, $hFrmMain)
    ; experimentell
    hideTextCursor()
    openFileDialog()
    ; Event Listener
    While 1
    ; experimentell
    hideTextCursor()
    Switch GUIGetMsg()
    ; Button "Datei öffnen"
    Case $hBtnOpen
    btn_open_click()
    ; Button "Datei erneut prüfen"
    Case $hBtnReCheck
    btn_recheck_click()
    ; Button "Zwischenablage"
    Case $hBtnClipBoard
    btn_clip_click($hTextBox)
    ; "Beenden" Button bzw. Event
    Case $hBtnClose, $GUI_EVENT_CLOSE
    btn_and_event_close_click()
    ; Datei via Drag and Drop auf Textbox
    Case $GUI_EVENT_DROPPED
    file_dropped(@GUI_DragFile)
    EndSwitch
    WEnd
    EndFunc

    [/autoit]


    Achja, meine Controls (Elemente) sind alle absichtlich global, das soll so sein. Mir ging es um $globFullFilePath.

    Danke mal an alle.

  • Ohne global in diesem Fall

    • hausl78
    • 21. April 2014 um 23:22

    Hallo!

    Wie ich hier auch schon öfters gelesen habe solte man ja global verhindern, Seiteneffekte auf Grund des "zuballerns" des Namensraumes etc.. Nun Hätte ich für ein spezielles Szenario eine Frage wie ich das hinbekomme:

    Ich habe eine GUI, darin kann man u.a. eine Datei öffnen, es werden einige Prüfungen durchgeführt und dann quasi ein ErgebnisLog angezeigt. Nun gehen wir mal davon aus, der Anwender (das will ich so haben!) öffnet die geprüfte Datei parallel im Edior und fixt einen nach den anderem Fehler.

    Nun gibt es in der GUI einen Button "Datei erneut prüfen" dieser prüft nach klick die vorhin bereits geprüfte Datei erneut. Und da liegt mein Hund begraben. Wie bekomme ich ohne globals den Dateinamen vom ersten "Datei öffnen" Vorgang nun zu der Funktion die der Button "Datei erneut prüfen" ausführt? MIr ist der Weg des Pfades nicht klar, den dieser als Parameter über die Funktion geht die die GUI aufbaut mit der (ich nenn die so) "Listener-Endlos-Schleife".

    Ich bekomme es ohne global nicht hin.. Ist jetzt keine Tragik, aber ich wollte es hier gerne mal gesagt haben, ev. fehlt mir ja noch dies oder das zum logischen Aufbau/Workflow von so einem "Tool"

    Danke!

Spenden

Jeder Euro hilft uns, Euch zu helfen.

Download

AutoIt Tutorial
AutoIt Buch
Onlinehilfe
AutoIt Entwickler
  1. Datenschutzerklärung
  2. Impressum
  3. Shoutbox-Archiv
Community-Software: WoltLab Suite™