Umlaute in Dateien aus DOS-Befehlen

  • Nach längerer Suche frage ich doch lieber:

    Die Ausgabe von einem DOS-Dir-Befehl in eine Datei macht Probleme mit den Umlauten. Alles Mögliche versucht, um beim Öffnen der Datei mit Excel oder Notepad++ eine korrekte Darstellung zu erhalten sind gescheitert. Wie kann ich entweder die Ausgabe von Dir direkt mit richtigen Umlauten erzeugen oder alternativ wie kann ich eine Datei mit den "falschen" Umlauten "übersetzen"?

    Ich habe eine Function von AspirinJunkie gefunden, die das vielleicht kann. Aber die bricht immer ab in der Zeile
    $s_Ret = DllCall($h_User32DLL, 'BOOL', 'OemToChar', 'str', $s_Ret, 'str', '')[2] mit

    !>14:13:26 AutoIt3.exe ended.rc:-1073741819. Was mache ich falsch?


    :)

  • Ist J:\... ein Netzlaufwerk?

    verwendest du eine Batch? (.bat oder .com) ?

    Wie erstellst du die Batch Datei?

    Wenn mit Notepad++, dann die Sprache auf MS-Dos Style ändern und dann erst abspeichern.

    In der Batch Datei würde ich folgendes schreiben:

    Code
    chcp 1252
    Dir C:\.... > Ausgabe.txt

    Bei der Lösung von Aspirinjunkie kann ich mir vorstellen, dass Netzlaufwerke ein Problem darstellen könnten.

    Das Script läuft auch mit Netzlaufwerken. ^^

    Der von dir geschilderte Fehler ist i.d.R. ein "harter" Error eines Dll-Aufrufs, der mit irgendeinem Parameter an die Wand gefahren ist.

    ich hab die Funktion von AspirinJunkie mal verschlankt und optimiert.

    MfG Schnuffel

    "Sarkasmus ist die niedrigste Form des Witzes, aber die höchste Form der Intelligenz."
    Val McDermid

    ein paar Infos ...

    Wer mehr als "nur" Hilfe benötigt, kann sich gern im Forum "Programmieranfragen" an uns wenden. Wir helfen in allen Fällen, die die Forenregeln zulassen.

    Für schnelle Hilfe benötigen wir ein ! lauffähiges ! Script, dass wir als Demonstration des Problems testen können. Wer von uns erwartet ein Teilscript erstmal lauffähig zu bekommen, der hat
    1. keine wirkliche Not
    2. keinen Respekt vor Menschen die ihm in ihrer Freizeit Ihre Hilfe anbieten
    3. oder ist einfach nur faul und meint wir coden das für ihn

    In solchen Fällen erlaube ich mir, die Anfrage einfach zu ignorieren. ;)

    6 Mal editiert, zuletzt von Schnuffel (2. März 2024 um 15:15)

  • Das Problem ist, dass OemToChar keine Eingangsstrings mit einer Größe > 65536 verträgt.
    Das könnte man lösen, indem man den Eingangsstring entsprechend aufsplittet und schrittweise konvertiert.
    Aber genau das haben die Programmierer der von AutoIt mitgelieferten Funktion _WinAPI_OemToChar() bereits schon so umgesetzt.
    Daher kann man auch direkt diese Funktion stattdessen verwenden.
    Ich selbst habe auch die Funktion mit der ich sowas ausführe mittlerweile entsprechend umgebaut:

  • Ok um die Problemlage mal noch etwas detaillierter zu beschreiben.
    Das eigentliche Problem ist nicht die Größe des Eingangsstrings.
    Stattdessen ist es die Größe des Puffers für den Ausgabestring.
    Das ist der 2. Parameter der Funktion und im problematischen Aufruf, wurde dieser einfach als Leerstring des Typs "str" definiert.
    Das führt dazu, dass AutoIt hierfür einen Speicherbereich von 65536 Zeichen reserviert.
    Klar - AutoIt kann ja auch nicht wissen wie groß der Speicher sein muss.

    Wenn der Eingabestring nun größer als diese 65536 Zeichen ist, dann reicht dieser Platz also nicht aus und wir laufen in den beobachteten Fehler.

    Kann man was dagegen tun?`
    Ja - man kann selbst einen Puffer erzeugen und diesem die korrekte Größe zuweisen.
    Das sähe dann ungefähr so aus:

    Einmal editiert, zuletzt von AspirinJunkie (2. März 2024 um 17:42)

  • Code
    Global $sSubOrdner = "Allgemein", $aSearchDeli= "_", $sFilePath = "J:\Kunden\Error.txt"
    
    $sSearch = @ComSpec & ' /c dir J:\Kunden\*' & $aSubOrdner[$i] & ' /s/b/ad |find "' & $aSearchDeli[$j] & '" >> ' & $sFilePath
    $iPID = Run($sSearch, "", @SW_HIDE)
    ProcessWaitClose($iPID)

    So erzeuge ich die Datei.

    :)

  • Code
    Global $aArray = _FileListToArrayRec($sSearchDir, "Allgemein\;Daten\", $FLTAR_FOLDERS, $FLTAR_RECUR, $FLTAR_NOSORT, $FLTAR_RELPATH)

    Bin auf diesen Befehl gestoßen, mit dem ich alle Verzeichnisse sammeln will, die unter einem Ordner "Allgemein" oder "Daten" noch Unterverzeichnisse haben. Mit dem \ in der Maske bekomme ich aber @extended = 2 wogegen der Befehl mit "Allgemein;Daten" ohne \ funktioniert, mir dann aber auch zusätzlich alle Verzeichnisse sammelt, die mit ...\Allgemein und ...\Daten enden. Fehler in der Function? Kann man das umgehen?

    :)

  • wenn du 2 bestimmte Ordner rekursive erfassen möchtest, würde ich das so lösen:

    AutoIt
    Global $sSearchDir = "C:\Windows"
    Global $aDir_Allgemein = _FileListToArrayRec($sSearchDir & "\Web", "*", 2, 1)
    Global $aDir_Daten = _FileListToArrayRec($sSearchDir & "\Help", "*", 2, 1)
    _ArrayConcatenate($aDir_Allgemein, $aDir_Daten, 1)
    $aDir_Allgemein[0] = UBound($aDir_Allgemein) - 1
    _ArrayDisplay($aDir_Allgemein)

    musst halt die Ordner und des $sSearchDir an deine Bedürfnisse abändern

    PS: der Parameter $iReturn kann dir auch deine Junktions liefern ;)

    + $FLTAR_NOLINK (16) - Link/junction folders

    MfG Schnuffel

    "Sarkasmus ist die niedrigste Form des Witzes, aber die höchste Form der Intelligenz."
    Val McDermid

    ein paar Infos ...

    Wer mehr als "nur" Hilfe benötigt, kann sich gern im Forum "Programmieranfragen" an uns wenden. Wir helfen in allen Fällen, die die Forenregeln zulassen.

    Für schnelle Hilfe benötigen wir ein ! lauffähiges ! Script, dass wir als Demonstration des Problems testen können. Wer von uns erwartet ein Teilscript erstmal lauffähig zu bekommen, der hat
    1. keine wirkliche Not
    2. keinen Respekt vor Menschen die ihm in ihrer Freizeit Ihre Hilfe anbieten
    3. oder ist einfach nur faul und meint wir coden das für ihn

    In solchen Fällen erlaube ich mir, die Anfrage einfach zu ignorieren. ;)

    Einmal editiert, zuletzt von Schnuffel (3. März 2024 um 13:13)

  • Danke, aber ich denke ich muss das noch anders lösen. Im Verzeichnis sind mehr als 100.000 Unterverzeichnisse und ich habe 12 Suchbegriffe (nicht nur die beiden). Das dauert wenn ich die alle 12 nacheinander durchgehe ... <X

    Neue Idee: ich sammle sämtliche Ordner ohne Einschränkung in einem Array und laufe dann einmal über das Array und selektiere die gewünschten Ordner dabei.

    Noch mal zur Info warum ich das mache:

    ich suche immer noch in diesem Riesenverzeichnis die Unterverzeichnisse, die von irgendjemandem mit der Maus ungeschickt in ein anderes Verzeichnis verschoben wurden (habe aus einer Zeit von einem Jahr schon 5-fache Verschachtelungen untereinander gefunden. Wenn in einem Ordnerpfad 2 oder mehr von meinen Suchbegriffen sind, dann ist das einer der Treffer. Oder wenn ein Verzeichnis in der ersten Ebene keine Ordner und Daten hat.

    Wenn ich dann noch ohne großen Aufwand zu diesen gefundenen Ordnern den jeweiligen "Besitzer" abfragen und in mein Array bekommen könnte, dann hätte ich auch eine Statistik, wer jeweils der Verursacher war. Hat jemand dazu eine Idee?

    Danach muss ich in einem anderen Verzeichnis alle Unterordner finden, in denen eine Junction ist (eine Junction darf nur in der ersten Ebene sein, die besteht nur aus Junctions). Das ist aber nur ein untergeordnetes Problem, die stören nicht wirklich.

    AspirinJunkie _prc_RunWithReturn() funktioniert einwandfrei, kann ich an anderer Stelle gut gebrauchen.

    :)

  • hier noch ein Ansatz. Aber bitte erst ausprobieren an einem nicht so großen Verzeichnis...

    Das Array sollte alle Ordner rekursiv liefern alphabetisch sortiert, den Besitzer, die Größe (im Moment nicht rekursiv), Anzahl Ordner und Anzahl Dateien.

    Wenn die Größe rekursiv ermittelt werden soll, dann in Zeile 19 die 3 mit einer 1 ersetzen ;)

    Bei fehlender Leseberechtigung auf einen Ordner bricht das Script bis jetzt ab. Das müsste man sich noch ansehen um das abzufangen.

    MfG Schnuffel

    "Sarkasmus ist die niedrigste Form des Witzes, aber die höchste Form der Intelligenz."
    Val McDermid

    ein paar Infos ...

    Wer mehr als "nur" Hilfe benötigt, kann sich gern im Forum "Programmieranfragen" an uns wenden. Wir helfen in allen Fällen, die die Forenregeln zulassen.

    Für schnelle Hilfe benötigen wir ein ! lauffähiges ! Script, dass wir als Demonstration des Problems testen können. Wer von uns erwartet ein Teilscript erstmal lauffähig zu bekommen, der hat
    1. keine wirkliche Not
    2. keinen Respekt vor Menschen die ihm in ihrer Freizeit Ihre Hilfe anbieten
    3. oder ist einfach nur faul und meint wir coden das für ihn

    In solchen Fällen erlaube ich mir, die Anfrage einfach zu ignorieren. ;)

    Einmal editiert, zuletzt von Schnuffel (3. März 2024 um 15:42)

  • Klappt perfekt, aber Problem mit Umlauten im Pfad - da muss ich wohl die Function von AspirinJunkie reinmischen!? Oder kann man das nicht direkt innerhalb AutoIt in ein Array einlesen?

    :)

  • Code
    Global $aOutput = _FileListToArrayRec($sSearchDir, "*", $FLTAR_FOLDERS, $FLTAR_RECUR, $FLTAR_NOSORT, $FLTAR_FULLPATH)

    So klappt es - danke !

    Allerdings eliminiere ich im ersten Schritt alle Verzeichnisse, die nicht verschoben sind und beim Rest - ca. 100-300 - hole ich mir den Owner. Wochenende gerettet ;)

    :)

    Einmal editiert, zuletzt von HansJ54 (3. März 2024 um 16:25)

  • 4 1/2 Minuten für über 100.000 Verzeichnisse mit _FileListToArrayRec() ohne DOS-Dir. EIn Problem war, dass ArrayAdd() ab über 10.000 Einträgen immer langsamer wurde und endlos dauerte. Daher alles sukzessive in vordefinierte Arrays gespeichert.
    Jetzt bleibt nur noch das Restproblem, dass die gefundenen und in einer .csv gespeicherten kritischen Pfade (_FileWriteFromArray() nach _FileListToArrayRec() ) wieder Probleme mit den Umlauten haben. Wie kann ich die konvertieren?

    Kann ich dafür irgendwie $aCall = DllCall("user32.dll", 'BOOL', 'OemToCharA', 'PTR', DllStructGetPtr($tIn), 'PTR', DllStructGetPtr($tOut)) nutzen? Oder kann ich direkt eine passende Codepage irgendwie benutzen?

    :)

  • EIn Problem war, dass ArrayAdd() ab über 10.000 Einträgen immer langsamer wurde und endlos dauerte.

    Das ist eine erwartbare Eigenschaft von _ArrayAdd() und schlägt öfters mal als Problem auf.
    Hintergrund ist, dass bei jedem Aufruf von _ArrayAdd ein neuer größerer Speicherbereich alloziert wird (ReDim) und das bisherige Array dorthin verschoben wird - jedes einzelne mal.
    Je mehr Elemente im Array stehen umso länger dauert das halt.

    Möglichkeiten das schneller zu gestalten als einzelne Werte mit _ArrayAdd hinzuzufügen:

    • Wenn nicht nur 1 Element hinzugefügt werden soll sondern mehrere gleichzeitig, dann kann man _ArrayAdd auch ein Array übergeben - so wird nur einmal ein ReDim durchgeführt).
    • Maps sind dynamischer. Man kann sich auch damit mit Hilfe von MapAppend() schrittweise eine entsprechende Datenstruktur aufbauen und am Ende diese, falls notwendig, in ein Array überführen.
    • Arrays können auch dynamischer gestaltet werden, wenn man dessen Größe flexibler anpasst. Nennt sich dann ArrayList und wurde auch in AutoIt mal umgesetzt: >>DynArray-UDF<<

    Jetzt bleibt nur noch das Restproblem, dass die gefundenen und in einer .csv gespeicherten kritischen Pfade (_FileWriteFromArray() nach _FileListToArrayRec() ) wieder Probleme mit den Umlauten haben.

    Bei _FileListToArrayRec() selbst sollte es keine Probleme mit Umlauten geben. Wenn du dessen Ergebnis erst in eine Datei schreibst und wieder ausliest (warum eigentlich?) liegt das Problem an dieser Stelle.
    Achte darauf, dass sowohl beim Schreiben als auch Lesen dieser Dateien die gleiche Kodierung verwendet wird. Am besten eine mit BOM. $FO_UTF8 wäre als Modus für FileOpen() daher geeignet.

    Die OemToChar bringt hierbei nichts, da diese einen OEM-kodierten String übersetzt. Diese Kodierung wird aber fast nur bei den Windows-Konsolenbefehlen verwendet.
    Und da du ja kein "DOS"-Dir (es gibt schon seit Windows XP kein DOS mehr) verwendest, brauchst du diese Konvertierung daher nicht.

  • Zuerst noch mal: vielen Dank für Eure Hilfe - zur Information über mich: ich habe im Studium noch mit Lochkarten gearbeitet ...

    DOS-Dir: alles was ich über das "schwarze CMD-Fenster" abschicke ist für mich gefühlt noch DOS 8)

    Array: ich habe einfach ein großes Array definiert mit 120.000 und nachher mit ReDim angepasst. Das reduzierte die Zeit von 6 Stunden auf die 4 1/2 Minuten. Wenn ich vorher _DynArray-UDF gekannt hätte, wäre es sicher einfacher gewesen.;)

    Array: wie kann ich bei einem Array[20][2] eine dritte Spalte zufügen, ohne den Inhalt zu verlieren? Bei ReDim war der Inhalt weg. Geht das ohne For $i ... und einem neuen Array?

    BOM (Byte Order Mark - musste ich erst mal googeln): _FileListToArrayRec() passt für _ArrayDisplay() in AutoIt, aber anschließend schreibe ich mit _FileWriteFromArray($sFileNegPath, $aNegative, 1) in eine .csv um mit Excel weiterzuarbeiten. Da sind dann beim Öffnen die Umlaute falsch. Nach dem BOM habe ich mit dem Hexeditor geschaut: kein BOM vorhanden. Jetzt brauche ich noch Hilfe, wie ich ein Array speichern kann mit einem passenden BOM. Bei FileOpen() gibt es den Parameter, bei _FileWriteFromArray() scheinbar nicht. Array in String umwandeln und dann mit FileOpen/FileWrite schreiben oder gibt es eine bessere Lösung?

    :)

  • Array: ich habe einfach ein großes Array definiert mit 120.000 und nachher mit ReDim angepasst. Das reduzierte die Zeit von 6 Stunden auf die 4 1/2 Minuten. Wenn ich vorher _DynArray-UDF gekannt hätte, wäre es sicher einfacher gewesen. ;)

    Nein da musst du nicht erst mit einer extra UDF rumbasteln.
    Dein Weg führt für deinen Fall einfacher und schneller ans Ziel.
    Das bisschen Speichermehrverbrauch ist hierbei zu verschmerzen.

    Array: wie kann ich bei einem Array[20][2] eine dritte Spalte zufügen, ohne den Inhalt zu verlieren? Bei ReDim war der Inhalt weg. Geht das ohne For $i ... und einem neuen Array?

    Bei mir bleibt der Inhalt bestehen:

    AutoIt
    #include <Array.au3>
    
    Global $aArray[4][2] = [[1,2],[3,4],[5,6],[7,8]]
    _ArrayDisplay($aArray, "vorher")
    
    Redim $aArray[4][3]
    _ArrayDisplay($aArray, "nachher")

    in eine .csv um mit Excel weiterzuarbeiten. Da sind dann beim Öffnen die Umlaute falsch.

    Ja dann ist es ja kein Problem von AutoIt sondern von Excel, welches zu dumm ist eine Kodierung korrekt zu erkennen.
    Damit auch Excel als letzter es rafft bietet es sich an die Datei mit BOM zu speichern:

    Bei FileOpen() gibt es den Parameter, bei _FileWriteFromArray() scheinbar nicht.

    _FileWriteFromArray() nimmt entweder einen Pfad oder eben ein FileHandle wie es von FileOpen() zurückgegeben wird.
    Sprich: die Datei manuell zum Schreiben in der richtigen Kodierung mit FileOpen() öffnen und mit dem Handle _FileWriteFromArray() füttern:

  • Klappt alles perfekt, nochmals vielen Dank!


    Bis auf ReDim, da habe ich mich wohl nicht exakt ausgedrückt - in meinem ersten Beispiel wird der alte Inhalt des eindimensionalen Arrays gelöscht. Aber ich habe _ArrayColInsert() gefunden, damit geht es natürlich (vorher vermutlich mal wieder nicht sinnvoll gesucht).

    Code
    #include <Array.au3>
    
    Global $aArray[4] = [1,3,5,7]
    _ArrayDisplay($aArray, "vorher")
    
    Redim $aArray[4][2]
    _ArrayDisplay($aArray, "nachher")
    Code
    #include <Array.au3>
    
    Global $aArray[4] = [1,3,5,7]
    _ArrayDisplay($aArray, "vorher")
    
    _ArrayColInsert($aArray,1)
    _ArrayDisplay($aArray, "nach ColInsert")

    :)

  • in meinem ersten Beispiel wird der alte Inhalt des eindimensionalen Arrays gelöscht.

    Das ist was anderes als eine 3. Spalte hinzufügen.
    Bei Änderungen der Dimensionsanzahl geht bei ReDim immer der Inhalt verloren.
    _ArrayColInsert macht hierbei auch nichts anderes als du manuell: Es kopiert den Inhalt einzeln in ein neues Array.

  • Noch was für die Spezialisten: jetzt muss ich noch alle Verzeichnisse suchen, die nicht im Root sind, also irgendwo tiefer, in denen irgendwo _nnnn vorkommt, also ein Unterstrich und 4 Zahlen. Es würde mir aber auch schon reichen, alle _nnn überhaupt zu filtern. Kann man bei _FileListToArrayRec() irgendetwas mit RegEx machen?

    :)

  • Bei _FileListToArrayRec nicht, das hat nur die "Mask" und die ist eher eingeschränkt.

    Man kann das ganze aber einfach selbst implementieren:

  • Ich hab für soetwas auch eine entsprechende Funktion rumliegen, welche neben der Möglichkeit mit regulären Ausdrücken zu filtern auch die Option bietet mit komplett eigenen Funktionen zu filtern.
    Für deinen Fall reicht der RegEx-Filter ja aber aus.
    Warum könnte die Funktion für dich dennoch etwas sein, obwohl du bereits die schlanke, elegante Variante von Kanashius vorliegen hast?
    Du sagtest letztens, dass du mehrere Ordner durchsuchen musst und daher mehrmals die Funktion aufrufen und die Arrays am Ende zusammenfügen musst.

    Mit der folgenden Funktion kannst du direkt mehrere Ordner (durch "|" getrennt) übergeben und dir damit evtl das Leben ein bisschen einfacher gestalten:

    Edit: Ich erinnere mich, dass du junctions finden wolltest. Die Funktion hier filtert aber ganz bewusst junctions (in dem Fall alle reparse points) heraus.
    Müsstest du also mal entsprechend testen.

    Einmal editiert, zuletzt von AspirinJunkie (6. März 2024 um 06:33)