Funktion zum Zeichen ersetzen nachbasteln

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
Stolzi
User
Beiträge: 155
Registriert: Mittwoch 18. August 2004, 15:44

Hi Leute... ich bräuchte wiedermal Hilfe von jemand selbstlosen, denn für mich ist das zu hoh.
Ich muss einen String umwandeln, dass er mit dem von einem anderen Programm erzeugten ident ist. Ich habe das bisher so gemacht:

Code: Alles auswählen

NameInclOrd = re.sub(r"['\"\\/:*?|<>#]", lambda m: "#"+m.group(0).encode("hex"), NameInclOrd) 
Nun kommen aber immer wieder Zeichen dazu... Gerade zB habe ich gesehen, dass ich è auch umsetzen muss. Daher ist glaube ich der einzig richtige Weg zu schaun wies in dem anderen Programm gemacht wird und das gleich zu machen. Und da habe ich auch gesehen, dass da vielmehr gesagt wird was _nicht_ umgesetzt werden soll. Ich schaff das nur leider nicht das schön nach zu basteln... kann auch diesen C Code schlecht lesen.
Ist jemand so nett mir zu helfen?

Danke
Stolzi

Das müsste ich nachbauen:

Code: Alles auswählen

if (ToFileSystem) {
              switch (*p) {
                     // characters that can be used "as is":
                     case '!':
                     case '@':
                     case '$':
                     case '%':
                     case '&':
                     case '(':
                     case ')':
                     case '+':
                     case ',':
                     case '-':
                     case ';':
                     case '=':
                     case '0' ... '9':
                     case 'a' ... 'z':
                     case 'A' ... 'Z':
                     case 'ä': case 'Ä':
                     case 'ö': case 'Ö':
                     case 'ü': case 'Ü':
                     case 'ß':
                          break;
                     // characters that can be mapped to other characters:
                     case ' ': *p = '_'; break;
                     case '~': *p = '/'; break;
                     // characters that have to be encoded:
                     default:
                       if (*p != '.' || !*(p + 1) || *(p + 1) == '~') { // Windows can't handle '.' at the end of directory names
                          int l = p - s;
                          s = (char *)realloc(s, strlen(s) + 10);
                          p = s + l;
                          char buf[4];
                          sprintf(buf, "#%02X", (unsigned char)*p);
                          memmove(p + 2, p, strlen(p) + 1);
                          strncpy(p, buf, 3);
                          p += 2;
                          }
                     }
              }
Edit (Leonidas): Highlighting-Sprache geändert.
Benutzeravatar
birkenfeld
Python-Forum Veteran
Beiträge: 1603
Registriert: Montag 20. März 2006, 15:29
Wohnort: Die aufstrebende Universitätsstadt bei München

OMG, das ist ja nett. "é" muss enkodiert werden, aber "ä" nicht?

Naja, die Lösung ist hier recht einfach:

Code: Alles auswählen

NameInclOrd = re.sub(r"[^-!@$%&()+,;=0-9a-zA-ZäöüÄÖÜß ~]", lambda m: "#"+m.group(0).encode("hex"), NameInclOrd).replace(" ", "_").replace("~", "/")
Sprich, alle Zeichen, die *nicht* in dem Zeichenbereich sind (die Negation bewirkt der "^" am Anfang des Zeichenbereichs), werden enkodiert.
Dann lieber noch Vim 7 als Windows 7.

http://pythonic.pocoo.org/
Stolzi
User
Beiträge: 155
Registriert: Mittwoch 18. August 2004, 15:44

Coll, danke! Werd ich versuchen.
Jo, das hat mit de sonderbaren Dateisystem Fat zu tun ;-)
Stolzi
User
Beiträge: 155
Registriert: Mittwoch 18. August 2004, 15:44

Leider ists das noch nich ganz:
aus

Code: Alles auswählen

[u'Nadia~Russisch, bitte!~Ein Sprachkurs f\xfcr Anf\xe4nger (14/30)']
Traceback (most recent call last):
  File "ListBook.py", line 49, in ?
    frame = MyFrame(None, wx.ID_ANY, 'VDR Verwaltung', sys.stdout)
  File "ListBook.py", line 41, in __init__
    self.LB=TestLB(self,wx.ID_ANY,self.log)
  File "ListBook.py", line 28, in __init__
    win=GridCustTable2.MyPanel(self, sys.stdout)
  File "/home/stolzi/Documents/Scripte/TVProj/GridCustTable2.py", line 292, in __init__
    self.grid = CustTableGrid(self.p, self.log)
  File "/home/stolzi/Documents/Scripte/TVProj/GridCustTable2.py", line 197, in __init__
    self.table = CustomDataTable(log)
  File "/home/stolzi/Documents/Scripte/TVProj/GridCustTable2.py", line 66, in __init__
    self.SetData()
  File "/home/stolzi/Documents/Scripte/TVProj/GridCustTable2.py", line 72, in SetData
    Data  = self.VDRclass.ShowAufnahmen(Mod)
  File "/home/stolzi/Documents/Scripte/TVProj/VdrData.py", line 355, in ShowAufnahmen
    NameInclOrd = re.sub(r"[^-!@$%&()+,;=0-9a-zA-ZÀöÌÃÃÃà ~]", lambda m: "#"+m.group(0).encode("hex"), NameInclOrd).replace(" ", "_").replace("~", "/")
  File "/usr/lib/python2.4/sre.py", line 142, in sub
    return _compile(pattern, 0).sub(repl, string, count)
  File "/home/stolzi/Documents/Scripte/TVProj/VdrData.py", line 355, in <lambda>
    NameInclOrd = re.sub(r"[^-!@$%&()+,;=0-9a-zA-ZÀöÌÃÃÃà ~]", lambda m: "#"+m.group(0).encode("hex"), NameInclOrd).replace(" ", "_").replace("~", "/")
  File "/usr/lib/python2.4/encodings/hex_codec.py", line 24, in hex_encode
    output = binascii.b2a_hex(input)
UnicodeEncodeError: 'ascii' codec can't encode character u'\xfc' in position 0: ordinal not in range(128)
(die erste Zeile ist der Wert den ich mit print [Wert,] ausgebe)
Verstehe nicht was ich da beim Encoding anders machen muss. Der String ist ja schon unicode wie man sieht. Das müsste doch passen, oder?

Danke dir
Benutzeravatar
birkenfeld
Python-Forum Veteran
Beiträge: 1603
Registriert: Montag 20. März 2006, 15:29
Wohnort: Die aufstrebende Universitätsstadt bei München

Das ist eben das Problem. Das "hex"-encoding arbeitet mit Bytes, nicht mit (Unicode-)Zeichen. Du musst den String also vorher enkodieren, wohl in Latin-1.
Dann lieber noch Vim 7 als Windows 7.

http://pythonic.pocoo.org/
rayo
User
Beiträge: 773
Registriert: Mittwoch 5. November 2003, 18:06
Wohnort: Schweiz
Kontaktdaten:

Hi

Mach doch einfach von Unicode encode nach FileSystem-Encoding (latin-1 oder so ein cp-irgendwieviel) und dann Zeichen ersetzen die ungültig im Dateinamen sind (| < > ! / \ : * " ?)

Gruss
Stolzi
User
Beiträge: 155
Registriert: Mittwoch 18. August 2004, 15:44

@rayo: Es geht darum, dass ich genau den selben String habe wie das andere Programm

@birkenfeld: Jo,so wills. Musste aber jetzt noch "_" hinzufügen. "." müsste auch noch dazu, aber da mault er ob ich ".", oder "\." schreibe. Wie gebe ich denn den . an im Ausdruck?
Benutzeravatar
birkenfeld
Python-Forum Veteran
Beiträge: 1603
Registriert: Montag 20. März 2006, 15:29
Wohnort: Die aufstrebende Universitätsstadt bei München

Da darf er nicht maulen. Innerhalb von [...] hat ein Punkt keine Sonderbedeutung.

Du darfst ihn aber nicht zwischen die [^- am Anfang setzen. Sonst gibt es Probleme, da beide Zeichen da stehen müssen (das "^", weil es die Zeichenklasse negiert und das "-", weil es sonst einen Zeichenbereich innerhalb der Zeichenklasse bedeuten würde).
Dann lieber noch Vim 7 als Windows 7.

http://pythonic.pocoo.org/
Stolzi
User
Beiträge: 155
Registriert: Mittwoch 18. August 2004, 15:44

Da hab ich irgendwie Mist gebaut... Hast recht. geht mit nur .
Nun hat sich noch ein Prob ergeben :-/ Nicht haun... ;-)
Ich gehe jetzt erst her und encode:

Code: Alles auswählen

NameInclOrd=AufnahmeName.encode(sys.getfilesystemencoding())
Hierzu mal eine Frage: Erst habe ich mir gedacht ich muss in das Encoding encoden in dem mein Skript geschrieben ist. Sprich am Anfang habe ich

Code: Alles auswählen

#!/usr/bin/python
#coding: utf-8
stehn und daher dachte ich

Code: Alles auswählen

NameInclOrd=AufnahmeName.encode("utf8")
wäre richtig. Dem war aber nicht so... Es scheint das sys.getfilesystemencoding() richtig zu sein. Stimmt das? Weisst du warum?

Mein eigentliches Problem: Ansich läufts so jetzt gut mit der Ausnahme, dass Umlaute umgesetzt werden. Das heisst er erkennt de Umlaut im String nicht als Umlaut wie in der Bedingung angegeben. Vermutlich weil ich den String encode? Was muss ich da tun, dass das mit den Umlauten auch klappt?

Danke
Stolzi
BlackJack

Du musst `NameInclOrd` mit der Kodierung kodieren die die "ausserhalb" des Programms für Dateinamen verwendet wird.

Und Du must Dir endlich darüber klarwerden was der Unterschied zwischen Bytes und Unicode ist, und das es ausserhalb von Programmen nur Bytes geben kann. Dein Quelltext liegt als Bytes auf der Festplatte. Wenn Du ein 'ä' im Quelltext auf dem Bildschirm siehst, dann muss das im Speicher irgendwie in Bytewerte umgesetzt werden. Der Rechner kennt keine Buchstaben sondern nur Zahlen. Bei dem `re.sub()` siehst Du ein 'ä', Dein Quelltext scheint UTF-8 kodiert zu sein, also steht im Speicher die Bytekette '\xc3\xa4' für ein 'ä'. Wenn Du jetzt ein 'ä' in einer Unicodezeichenkette hast und die mit Deiner Systemkodierung in eine Bytekette umwandelst, dann ist das unter Windows wahrscheinlich 'cp1250' oder so ähnlich und es kommt folgendes Byte dabei heraus: '\xe4'.

Und das ein ``re.sub('[\xc3\xa4]', 'foo', '\xe4')`` nichts ersetzt sollte klar sein.
Stolzi
User
Beiträge: 155
Registriert: Mittwoch 18. August 2004, 15:44

OK danke.. tu ich mir echt hart das zu verstehen.
Leider komm ich aber dann immer noch nicht dahinter wie ich nun das Prob löse:
Mein eigentliches Problem: Ansich läufts so jetzt gut mit der Ausnahme, dass Umlaute umgesetzt werden. Das heisst er erkennt den Umlaut im String nicht als Umlaut wie in der Bedingung angegeben. Vermutlich weil ich den String encode? Was muss ich da tun, dass das mit den Umlauten auch klappt?
Mein Aufruf sieht so aus:

Code: Alles auswählen

NameInclOrd=AufnahmeName.encode(sys.getfilesystemencoding())
print [NameInclOrd,]
NameInclOrd = re.sub(r"[^-!@$%&()+,;=0-9a-zA-ZäöüÄÖÜß ~_.]", lambda m: "#"+m.group(0).encode("hex"), NameInclOrd).replace(" ", "_").replace("~", "/")
BlackJack

Im ersten Argument von `res.sub()` stehen UTF-8- kodierte Buchstaben als Bytekette. `NameInclOrd` enthältvor der dritten Zeile Buchstaben die mit der Systemkodierung als Bytes kodiert wurden. Damit passen die nicht mehr zusammen wenn die Systemkodierung nicht auch UTF-8 ist. Das 'ä' im ersten Argument wird durch andere Bytewerte repräsentiert als ein 'ä' im dritten Argument von `re.sub()`.

Eine Lösungsmöglichkeit wäre es das erste Argument als Unicode-Literal im Quelltext anzugeben und es explizit auch in die Systemkodierung zu bringen. Da muss man dann nur aufpassen, das die mit einem Byte pro Buchstabe kodiert, sonst funktioniert der reguläre Ausdruck nicht richtig, weil dort in jedes Byte einzeln betrachtet wird.
Stolzi
User
Beiträge: 155
Registriert: Mittwoch 18. August 2004, 15:44

So wills jetzt:

Code: Alles auswählen

NameInclOrd=AufnahmeName.encode("latin1")
Arg=u"[^-!@$%&()+,;=0-9a-zA-ZäöüÄÖÜß ~_.]"
Arg=Arg.encode("latin1")
NameInclOrd = re.sub(Arg, lambda m: "#"+m.group(0).encode("hex"), NameInclOrd).replace(" ", "_").replace("~", os.sep)
Nur fehlt auf diese Weise nun das "r" vor der Bedingung. Hab trotz nachlesen nicht gefunden was das ist. Aber scheint nicht so wichtig zu sein, denn es funzt.

Danke euch!
BlackJack

Falls Du das ``r`` vor der Zeichenkette meintest: Da hättest Du bei Zeichenketten nachlesen müssen. Das ``r`` kennzeichnet ein "rohes" (raw) Zeichenkettenliteral, d.h. der Backslash hat dort keine besondere Bedeutung. Beispiel '\n' ist *ein* Zeichen, nämlich ein Zeilenende, r'\n' sind *zwei* Zeichen, nämlich der Backslash und ein 'n'. Weil man öfter Backslashes in regulären Ausdrücken benötigt ist das ganz praktisch, da man sonst für einen einzelnen Backslash zwei schreiben müsste. Und wenn man im regulären Ausdruck einen Backslash schreiben möchte, dann muss man den auch escapen, ohne das ``r`` also *vier* Backslashes schreiben.
Antworten