Umlaute im Pfad bzw. im Dateinamen

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Gleitschirmflieger
User
Beiträge: 12
Registriert: Sonntag 4. Februar 2018, 13:44

Hallo Pythonians,

nachdem ich seit zwei Tagen Google (... und auch die Forum-Suche) bemüht und sehr viel über diverse codecs gelesen habe, bin ich der Lösung für mein Problem nicht wirklich nahe gekommen.
In der Hoffnung auf den entscheidenden Hinweis wende ich mich somit an Euch.

Vorweg:

Ich programmiere eine kleine Anwendung im Environment eines Drittanbieters und bin daher gezwungen Python 2.7 zu verwenden.
OS = Windows 10 64-Bit

Vereinfachte Darstellung meines Problems:

In einem Verzeichnis existieren Dateien die Umlaute im Dateinamen haben.
Lese ich das Verzeichnis mit "os.listdir" aus und gebe die ausgelesenen Stings mit "print" zurück so werden die Umlaute nicht korrekt wiedergegeben.

Die Dateinamen:

Test1_ü_.csv
Test2_ä_.csv
Test3_ö_.csv

Code: Alles auswählen

# -*- coding: utf-8 -*-

import os

for datei in os.listdir(r'C:\Users\username\Desktop\TestCSV'):
    print(datei)
... führt in Jupyter Notebook zu folgendem Ergebnis:

Test1_�_.csv
Test2_�_.csv
Test3_�_.csv

... in der Shell des Drittanbieters zu diesem:

Test1_³_.csv
Test2_õ_.csv
Test3_÷_.csv

Nach unzähligen Versuchen mit decoding/encoding und diversen codecs die ausnahmslos zu Fehlermeldungen führen bin ich mit meinem 'latin-1' am Ende. :cry:

Bin äußerst Dankbar für jeden Tip der mich in Richtung Lösung schubst.

Beste Grüße,
Frank
Zuletzt geändert von Gleitschirmflieger am Freitag 27. März 2020, 15:59, insgesamt 1-mal geändert.
__deets__
User
Beiträge: 14542
Registriert: Mittwoch 14. Oktober 2015, 14:29

Bitte mal die repr-Form der per listdir ausgelesenen Strings hier posten. Und natuerlich auch ueber welches OS wir hier reden.
Gleitschirmflieger
User
Beiträge: 12
Registriert: Sonntag 4. Februar 2018, 13:44

Hallo __deets__,

Danke für die schnelle Antwort und Dein Interesse.

OS ist Windows 10 64-Bit
"repr-Form" <-- ... da kann ich Dir nicht ganz folgen.

Gruß,
Frank
nezzcarth
User
Beiträge: 1635
Registriert: Samstag 16. April 2011, 12:47

Unter Windows wird doch intern viel utf-16 (ich glaube Low Endian) eingesetzt, während Python von utf-8 ausgehen sollte. Schau Mal, ob dir das weiter hilft.
Zuletzt geändert von nezzcarth am Freitag 27. März 2020, 16:08, insgesamt 2-mal geändert.
Gleitschirmflieger
User
Beiträge: 12
Registriert: Sonntag 4. Februar 2018, 13:44

Code: Alles auswählen

print(os.listdir(r'C:\Users\username\Desktop\TestCSV'))
... bringt in Jupyter Notebook folgendes Ergebnis:

['Test1_\xfc_.csv', 'Test2_\xe4_.csv', 'Test3_\xf6_.csv']
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

Mit latin-1 warst Du doch schon auf der richtigen Spur:

Code: Alles auswählen

>>> print '\xfc'
³
>>> print '\xfc'.decode('latin-1')
ü
__deets__
User
Beiträge: 14542
Registriert: Mittwoch 14. Oktober 2015, 14:29

repr-form heisst repr(string), repr ist eine Funktion. Kann man auch mal suchen, wenn einem der Begriff nicht bekannt ist. Implizit wird die aber bei der Ausgabe einer Liste schon genutzt (da wird bei print liste jedes einezelne Element mit repr(element) in einen String ueberfuehrt), und damit kann man arbeiten. Die Daten sind augenscheinlich latin-1 kodiert:

Code: Alles auswählen

Python 2.7.11 (v2.7.11:6d1b6a68f775, Dec  5 2015, 12:54:16)
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> l = ['Test1_\xfc_.csv', 'Test2_\xe4_.csv', 'Test3_\xf6_.csv']
>>> print("\n".join(e.decode('latin1') for e in l))
Test1_ü_.csv
Test2_ä_.csv
Test3_ö_.csv
Gleitschirmflieger
User
Beiträge: 12
Registriert: Sonntag 4. Februar 2018, 13:44

'latin1' funktioniert tatsächlich in Jupiter Notebook ... ich war der festen Überzeugung ich hätte auch das schon getestet. Sorry!

In der Shell schauts dann jedoch so aus:

Test1_├╝_.csv
Test2_ä_.csv
Test3_├Â_.csv

Ich nehme an ich muss rausfinden welcher der Standard Codec der Shell ist und die Strings damit wieder "encoden"
Gleitschirmflieger
User
Beiträge: 12
Registriert: Sonntag 4. Februar 2018, 13:44

Die repr()-Werte in der Shell:

u'Test1_\xfc_.csv'
u'Test2_\xe4_.csv'
u'Test3_\xf6_.csv'
__deets__
User
Beiträge: 14542
Registriert: Mittwoch 14. Oktober 2015, 14:29

Das sind schon unicode-objekte, da bringt das repr nix mehr. Hast du da schon encode drauf aufgerufen?
Benutzeravatar
__blackjack__
User
Beiträge: 13111
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Gleitschirmflieger: Versuch's mal mit Unicode. Wenn man `os.listdir()` eine Unicode-Zeichenkette übergibt, dann liefert das auch Unicode-Zeichenketten zurück: ``os.listdir(ur'C:\Users\username\Desktop\TestCSV')``. Und die sollten dann sowohl in Jupyter als auch in der Shell des Drittanbieters bei `print()` automagisch richtig kodiert werden. Sofern die Kodierung der Shell die Zeichen repräsentieren kann.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Gleitschirmflieger
User
Beiträge: 12
Registriert: Sonntag 4. Februar 2018, 13:44

Hallo,

@ __deets__:

ja, hab encode mit 'latin1' getestet:

Test1_³_.csv
Test2_õ_.csv
Test3_÷_.csv

@__blackjack__:

... das 'ur', also die Übergabe eines 'rawunicodeescape' codierten Strings an 'os.listdir' scheitert leider:

Code: Alles auswählen

# -*- coding: utf-8 -*-

import os

for datei in os.listdir(ur'C:\Users\rueth\Desktop\TestCSV'):
    #print(datei.decode('latin1'))
    print(datei)
    
File "<ipython-input-4-279cd3fd3fef>", line 5
for datei in os.listdir(ur'C:\Users\rueth\Desktop\TestCSV'):
SyntaxError: (unicode error) 'rawunicodeescape' codec can't decode bytes in position 2-3: truncated \uXXXX    
Benutzeravatar
__blackjack__
User
Beiträge: 13111
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Hm interessant. Die Kombination hatte ich selbst noch nie. Dann halt selbst die "\" escapen: u"C:\\Users\\rueth\\Desktop\\TestCSV"
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

Ist dass nun ein Bug oder ein Feature dass \U trotz Rawstring interpretiert wird?
Gleitschirmflieger
User
Beiträge: 12
Registriert: Sonntag 4. Februar 2018, 13:44

Hallo __Blackjack__,

ich habe den Pfad geändert und '\' gegen '/' getauscht.
Jetzt funktioniert's auch mit rawunicode:

Code: Alles auswählen

# -*- coding: utf-8 -*-

import os

for datei in os.listdir(ur'C:/Users/username/Desktop/TestCSV'):
    #print(datei.decode('latin1'))
    print(datei)
Ergebnis in Jupyter Notebook:

Test1_ü_.csv
Test2_ä_.csv
Test3_ö_.csv

Das decodieren mit 'latin1' kann ich mir in Jupyter jetzt sparen.
Zumindest schonmal eine Erkenntnis die ein klein wenig dem Verständnis beiträgt.

Was mein Problem in der Shell des Drittanbieters angeht hab ich noch keine Lösung.
Zum Test habe ich mal die mit 'os.listdir' ausgelesenen Dateinamen verwendet um gleichnamige Ordner anzulegen:

Code: Alles auswählen

# -*- coding: utf-8 -*-

import os

source_dir = r'C:\Users\username\Desktop\TestCSV'
target_dir = os.path.join(source_dir, r'Export')
for entry in os.listdir(source_dir):
        folder = os.path.join(target_dir, entry)
        if not os.path.exists(folder):
            os.makedirs(folder)
Auch ohne die 'raw-unicode' codierung funktioniert hier das anlegen der Ordner mit Umlauten im Ordnernamen.

Das bedeutet mein einziges Problem ist die Wiedergabe der Dateinamen in der Drittanbieter-Shell welche auch mit dem 'raw-unicode' String nicht funktioniert.
Zur Not würde ich zum Anzeigen der Dateinamen die Umlaute ersetzen (ü = ue).
Aber natürlich würde ich gern die Dateinamen so anzeigen wie sie auch tatsächlich sind, ... also mit Umlauten.
Da bin ich leider noch kein Stück weiter.

Gruß,
Frank
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

Was ist denn das für eine "Drittanbieter-Shell"? Python scheint dafür nicht das richtige Encoding ermitteln zu können.
Gleitschirmflieger
User
Beiträge: 12
Registriert: Sonntag 4. Februar 2018, 13:44

... und hier die Lösung:

Code: Alles auswählen

from __future__ import unicode_literals
Hiermit werden auch in der Drittanbieter-Shell die Dateinamen korrekt mit Umlaut angezeigt.

Vielen Dank für Hilfe, Interesse und Antworten:
@ __deets__
@ nezzcarth
@ Sirius3
@ __blackjack__
Benutzeravatar
__blackjack__
User
Beiträge: 13111
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Gleitschirmflieger: Das ist eine komische Lösung, beziehungsweise bedeutet die, dass Du irgendwo eine literale Byte-Zeichenkette verwendet hast, wo es eine literale Unicode-Zeichenkette hätte sein sollen.

Edit: Oder mal anders ausgedrückt, bei folgendem Code macht dieser Import keinen Unterschied:

Code: Alles auswählen

#!/usr/bin/env python
import os

for filename in os.listdir(u"some/path"):
    print filename
Wenn das bei Deinem Code einen Unterschied macht, dann sieht der anders aus, und das hast das Problem gelöst ohne zu wissen/verstanden zu haben wo das Problem liegt.

Und es kann natürlich jetzt sein das Du irgendwo in Probleme rennst die in die Gegenrichtung laufen, also die dadurch entstehen das jetzt irgendwo im Code Unicode-Zeichenketten stehen, die eigentlich Byte-Zeichenketten sein sollten.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Gleitschirmflieger
User
Beiträge: 12
Registriert: Sonntag 4. Februar 2018, 13:44

@ __blackjack__:

Wenn ich's richtig verstanden habe, dann hol ich mir mit "from __future__ import unicode_literals" einfach das standard Verhalten von Python3 in mein Python2 Modul.
Benutzeravatar
__blackjack__
User
Beiträge: 13111
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Gleitschirmflieger: Jain. Das Problem ist das das nur in Deinem Modul so ist, während die anderen Module ja immer noch Bytezeichenketten erwarten und liefern. Und immer wenn die irgendwie verbunden werden, darf die jeweilige Bytezeichenkette nur ASCII-Zeichen enthalten. Tut sie das nicht, dann klappt die implizite Konvertierung nicht. Ich fand diese Lösung mit `__future__` nicht so toll, denn um sich sicher zu sein das jedes Zeichenkettenliteral das macht was sie soll, muss man sich die alle anschauen und entscheiden ob das nun Bytes oder Text ist und welche Folgen das hat. Und wenn man das sowieso macht, kann man dann auch jedes Literal entsprechend mit einem `b` oder einem `u` kennzeichnen. Oder zumindest die `u`\s.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Antworten