Inhalt von 2 Dateien zusammen führen, nach Duplikaten suchen und löschen

  • Hallo,

    ich habe 2 csv-Dateien, diese würde ich gerne zusammenführen und danach doppelte einträge löschen. Hier kann es aber sein, dass nur die erste Spalte einer Zeile den selben Wert enthält, trotzdem soll dann die ganze Zeile gelöscht werden.

    Beispiel:

    Die erste Datei enthält:

    AAA;123;456;
    BBB;789;012;

    Die zweite Datei enthält:

    CCC;345;678;
    AAA;901;234;

    Zusammgeführt sollte es dann so aussehen:

    AAA;123;456;
    BBB;789;012;
    CCC;345;678;
    AAA;901;234;

    und dann sollte nach doppelten Werten in der ersten Spalte gesucht werden und dann die entsprechende Zeile gelöscht werden:

    BBB;789;012;
    CCC;345;678;

    Ich hatte hier so an _FileReadToArray gedacht. Es handelt sich aber um ca. 35000 Datensätze.

    Hat jemand sowas vielleicht schonmal gemacht und könnte ein Beispiel-Script posten?

    Vielen Dank.

  • Bitte schön. An deinem Beispiel hat es funktioniert :)

    Viel Spaß damit :D

    [autoit]

    #include <array.au3>
    #include <file.au3>

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

    $file=FileOpenDialog("",@DesktopDir,"All (*.*)")
    local $data[1]
    _FileReadToArray($file,$data,0)
    local $2DData[UBound($data)][3]
    for $i=0 to UBound($data)-1 step 1
    $ar=StringSplit($data[$i],";")
    $2DData[$i][0]=$ar[1]
    $2DData[$i][1]=$ar[2]
    $2DData[$i][2]=$ar[3]
    next
    $meToo=false
    for $i=0 to UBound($2DData)-1 step 1
    if $2DData[$i][0]<>"DeleteMe" then
    for $j=0 to UBound($2DData)-1 step 1
    if $i<>$j then
    if $2DData[$i][0]=$2DData[$j][0] then
    $2DData[$j][0]="DeleteMe"
    $meToo=true
    endif
    endif
    next
    endif
    if $meToo then
    $2DData[$i][0]="DeleteMe"
    $meToo=false
    endif
    next
    $string=""
    for $i=0 to UBound($2DData)-1 step 1
    if $2DData[$i][0]<>"DeleteMe" then
    $string&=$2DData[$i][0]&";"&$2DData[$i][1]&";"&$2DData[$i][2]&";"&@crlf
    endif
    next
    $fileHandle=FileOpen($file,2)
    FileWrite($fileHandle,$string)
    FileClose($fileHandle)
    exit

    [/autoit]
  • Hallo Kanashius,

    Wenn ich das Script starte, kommt ein Öffnen-Dialog und wenn ich dort eine Datei auswähle, erhalte ich eine Fehlermeldung:

    Line 6 _FileReadToArray($file,$data,0)
    ^ Error

    Error: Incorrect number of parameters in function call.

    Den FileOpenDialog könnten wir eigentlich auch weglassen, würde reichen, wenn er "Datei1.csv" und "Datei2.csv" lädt und dann das Ergebnis hinterher in "Datei3.csv" speichert.
    Das Script und die Dateien liegen alle in dem Ordner "Test" auf dem Desktop.

  • Welche AutoIt-Version verwendest du?
    Die aktuelle Version 3.3.10.2 hat bei FileReadToArray einen dritten Parameter welcher die Rückgabe der Anzahl der Zeilen am Index 0 unterdrückt (wird mit der nächsten AutoIt-Version aber schon wieder geändert).

    Wenn du die 0 bei FileReadToArray wegnimmst musst du dann halt in der nächsten Schleife am Index 1 statt 0 beginnen.

    Falls du dich doch für die aktuelle AutoIt-Version entscheiden solltest hätte ich auch noch meinen Vorschlag beizutragen:

    Spoiler anzeigen
    [autoit]

    Global Const $s_CSV1 = "Datei1.csv"
    Global Const $s_CSV2 = "Datei2.csv"
    Global Const $s_CSVOut = "Out.csv"

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

    Global $dic_CSV1 = ObjCreate("Scripting.Dictionary") ; Ein Dictionary als Zwischenspeicher da es sich schneller durchsuchen lässt als ein Array
    Global $s_Key, $s_Out = "" ; $s_Out = zu schreibendes Endergebnis

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

    For $s_Line In FileReadToArray($s_CSV1) ; erste Datei zeilenweise durchgehen
    $s_Key = StringLeft($s_Line, StringInStr($s_Line, ";")) ; erste Spalte auslesen
    $dic_CSV1($s_Key) = $s_Line ; erste Spalte als Key und ganze Zeile als Value in das Dictionary eintragen
    Next

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

    For $s_Line In FileReadToArray($s_CSV2) ; zweite Datei zeilenweise durchgehen
    $s_Key = StringLeft($s_Line, StringInStr($s_Line, ";")) ; erste Spalte auslesen
    If $s_Key == "" Then ContinueLoop ; Bei Fehler diesen Schleifendurchlauf überspringen
    If $dic_CSV1.Exists($s_Key) Then ; Check ob Wert schon in der ersten CSV vorhanden war
    $dic_CSV1.Remove($s_Key) ; Falls ja diesen Wert aus Dictionary löschen
    ContinueLoop ; Die Schleife hier abbrechen und beim nächsten Fortsetzen (=kein Schreiben auf $s_Out)
    EndIf
    $s_Out &= $s_Line & @CRLF ; Zeile zu $s_Out hinzufügen
    Next

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

    For $s_Key in $dic_CSV1.Keys() ; Alle Keys des Dictionarys durchgehen
    $s_Out &= $dic_CSV1($s_Key) & @CRLF ; Die mit den Keys verknüpften Daten (=die Zeilen) $s_Out ebenfalls noch hinzufügen
    Next

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

    ; $s_Out in Datei schreiben:
    FileDelete($s_CSVOut)
    FileWrite($s_CSVOut, StringTrimRight($s_Out, 2)) ; StringTrimRight um letztes unnötiges @CRLF zu entfernen

    [/autoit]

    Einmal editiert, zuletzt von AspirinJunkie (3. Juni 2014 um 14:08)

  • Welche AutoIt-Version verwendest du?
    Die aktuelle Version 3.3.10.2 hat bei FileReadToArray einen dritten Parameter welcher die Rückgabe der Anzahl der Zeilen am Index 0 unterdrückt (wird mit der nächsten AutoIt-Version aber schon wieder geändert).

    Also eigentlich ist die aktuelle Version die 3.3.12.0 :D

    There's a joke that C has the speed and efficieny of assembly language combined with readability of....assembly language. In other words, it's just a glorified assembly language. - Teh Interwebz

    C makes it easy to shoot yourself in the foot; C++ makes it harder, but when you do, you blow off your whole leg. - Bjarne Stroustrup
    Genie zu sein, bedeutet für mich, alles zu tun, was ich will. - Klaus Kinski

  • Eine Frage habe ich da doch noch,

    wie mache ich das, wenn ich eine ganze Zeile gelöscht haben will, sobald in der zweiten Spalte "0" steht?

    Bespiel:

    1;27;
    2;13;
    3;0;
    4;2;

    soll werden zu:

    1;27;
    2;13;
    4;2;

  • Der Code von Eukalyptus nimmt eine Zeichenkette und entfernt aus dieser alle Zeilen mit einer 0 im zweiten Feld.
    Da du die Quelldateien nie als ganzes in einer Zeichenkette hast (sondern hier nur zeilenweise) bietet es sich an diese Aktion mit der zu schreibenden Zeichenkette $s_Out durchzuführen.

  • ah ok, aber das bringt mir dann nichts, da die Datensätze vorher gelöscht werden müssten.

    Wäre es möglich diese Funktion

    Code
    $sNew = StringRegExpReplace($sStr, "(?m)^[^;]+;0+;.*\R|^(.*\R)", "$1")

    oder eine ähnliche noch vor dem eigentlichen Script durchzuführen (oder zur Not als eigenes Programm, welches ich dann mittels Batch starten kann).

    Die Funktion müsste auf jeden Fall bei der Quelldatei angewandt werden, damit entsprechende Datensätze vor dem Abgleich gelöscht werden.

    Vielen Dank.

    Einmal editiert, zuletzt von Bit-Service (5. Juni 2014 um 21:41)

  • Hallo,
    ich hänge mich mal mit hierein da mein Problem ähnlich ist.

    Als erstes muß ich eine .csv Datei (getrennt durch " | ") aufräumen, dazu möchte ich die doppler finden aber einen von den "dopplern" will ich behalten, welchen von den ist erstmal egal.
    Die .csv Datei ist wie bei meinen Vorgänger hier ebenso aufgebaut und ich habe auch die erste Spalte als Suchkriterium.

    Hier mal das gewünschte Ergebnis:

    test.csv --- Ausgangssituation

    Code
    1111|aa|bb|cc|
    2222|as|wd|34|
    1111|ab|bb|cd|
    3333|rt|er|io|

    test.csv --- nach entfernen der doppler

    Code
    1111|aa|bb|cc|
    2222|as|wd|34|
    3333|rt|er|io|

    Vielleicht kann mir hier einer auf die Sprünge helfen?

  • Dieses Programm funktioniert für dein Beispiel: (aussortiert wird nach der ersten Nummer)

    Spoiler anzeigen
    [autoit]

    #include <array.au3>
    #include <file.au3>
    $file=FileOpenDialog("",@DesktopDir,"All (*.*)")
    local $data[1]
    _FileReadToArray($file,$data,0)
    local $2DData[UBound($data)][4]
    for $i=0 to UBound($data)-1 step 1
    $ar=StringSplit($data[$i],"|")
    $2DData[$i][0]=$ar[1]
    $2DData[$i][1]=$ar[2]
    $2DData[$i][2]=$ar[3]
    $2DData[$i][3]=$ar[4]
    next
    $meToo=false
    $end=UBound($2DData)-1
    for $i=0 to $end step 1
    ConsoleWrite("Outer:"&$i&" "&$end&@crlf)
    for $j=0 to $i-1 step 1
    ConsoleWrite("Inner:"&$i&" "&$j&@crlf)
    if $2DData[$i][0]=$2DData[$j][0] then
    _ArrayDelete($2DData,$i)
    $end=$end-1
    if $i+1=$end or $i=$end then
    ExitLoop 2
    endif
    ExitLoop
    endif
    next
    next
    $string=""
    for $i=0 to UBound($2DData)-1 step 1
    $string&=$2DData[$i][0]&"|"&$2DData[$i][1]&"|"&$2DData[$i][2]&"|"&$2DData[$i][3]&"|"&@crlf
    next
    $fileHandle=FileOpen($file,2)
    FileWrite($fileHandle,$string)
    FileClose($fileHandle)
    exit

    [/autoit]
  • Hallo und danke für die schnelle Antwort,

    leider bekomme ich einen Fehler: EDIT: (eben gesehen das dieser Fehler mit der verwendeten AutoIt-Version kommt, ich habe v3.3.10.2)

    Code
    (25) : ==> Array variable has incorrect number of subscripts or subscript dimension range exceeded.:
    if $2DData[$i][0]=$2DData[$j][0] then
    if ^ ERROR

    hier das Script, nur ein wenig angepasst.

    Spoiler anzeigen
    [autoit]

    #include <array.au3>
    #include <file.au3>
    $file= @ScriptDir & "test.csv" ;FileOpenDialog("",@DesktopDir,"All (*.*)")
    local $data[1]
    _FileReadToArray($file,$data,0)
    local $2DData[UBound($data)][4]
    for $i=0 to UBound($data)-1 step 1
    $ar=StringSplit($data[$i],"|")
    $2DData[$i][0]=$ar[1]
    $2DData[$i][1]=$ar[2]
    $2DData[$i][2]=$ar[3]
    $2DData[$i][3]=$ar[4]
    ;~ $2DData[$i][4]=$ar[5]
    ;~ $2DData[$i][5]=$ar[6]
    ;~ $2DData[$i][6]=$ar[7]
    ;~ $2DData[$i][7]=$ar[8]
    ;~ $2DData[$i][8]=$ar[9]
    next
    $meToo=false
    $end=UBound($2DData)-1
    for $i=0 to $end step 1
    ConsoleWrite("Outer:"&$i&" "&$end&@crlf)
    for $j=0 to $i-1 step 1
    ConsoleWrite("Inner:"&$i&" "&$j&@crlf)
    if $2DData[$i][0]=$2DData[$j][0] then
    _ArrayDelete($2DData,$i)
    $end=$end-1
    if $i+1=$end or $i=$end then
    ExitLoop 2
    endif
    ExitLoop
    endif
    next
    next
    $string=""
    for $i=0 to UBound($2DData)-1 step 1
    $string&=$2DData[$i][0]&"|"&$2DData[$i][1]&"|"&$2DData[$i][2]&"|"&$2DData[$i][3]&"|"&@crlf ;$2DData[$i][4]&"|"&$2DData[$i][5]&"|"&$2DData[$i][6]&"|"&$2DData[$i][7]&"|"&$2DData[$i][8]&"|"&@crlf
    Next
    $fileHandle=FileOpen($file,2)
    FileWrite($fileHandle,$string)
    FileClose($fileHandle)
    exit

    [/autoit]

    Dann nochwas, die test.csv kann bis zu 8000 Zeilen umfassen, vielleicht wäre auch diese "Scripting.Dictionary" Variante möglich wie sie AspirinJunkie hier schon gepostet hatte, die müßte man noch anpassen so das sie halt nur eine Datei untersucht und auch einen "doppler" stehen läßt? Leider bin ich dazu noch nicht in der Lage.

    Ich hatte meine test.csv auch ein wenig falsch beschrieben:
    Diese hat max. 9 Trennzeichen( | ) und kann wie gesagt über ein paar tausend Zeilen groß sein.

    EDIT: eben gesehen das dieser Fehler mit der verwendeten AutoIt-Version kommen soll, ich habe v3.3.10.2
    Ein update wollte ich noch nicht machen weil sonst einige Excel-UDF nicht mehr gehen.

    Einmal editiert, zuletzt von Jensen.. (24. Juli 2014 um 19:39)

  • Könntest du das Array nicht auch _ArrayUnique aussortieren lassen?

    There's a joke that C has the speed and efficieny of assembly language combined with readability of....assembly language. In other words, it's just a glorified assembly language. - Teh Interwebz

    C makes it easy to shoot yourself in the foot; C++ makes it harder, but when you do, you blow off your whole leg. - Bjarne Stroustrup
    Genie zu sein, bedeutet für mich, alles zu tun, was ich will. - Klaus Kinski

  • Zitat von Hilfe

    Gibt ein Array zurück, welches alle Elemente maximal einmal enthält. Das Ergebnis ist ein 1-dimensionales Array.

    There's a joke that C has the speed and efficieny of assembly language combined with readability of....assembly language. In other words, it's just a glorified assembly language. - Teh Interwebz

    C makes it easy to shoot yourself in the foot; C++ makes it harder, but when you do, you blow off your whole leg. - Bjarne Stroustrup
    Genie zu sein, bedeutet für mich, alles zu tun, was ich will. - Klaus Kinski