Hi *, ich hänge hier an einem etwas lästigen Problem in einem Druckfilter, der intern mit unicode arbeitet, und an den IO Grenzen passend de/encodiert.. Als Option gibt's die Möglichkeit, das Ganze durch a2ps zu schleusen, wobei die Ausgabe dann in einer temp. Datei utf8 kodiert geparkt wird. Weil die Druckdaten auch noch Liniengrafik enthalten, muß ich bei der Ausgabe von utf8 nach cp850 wandeln, damit a2ps etwas damit anfangen kann. Aus Effizienzgründen verarbeite ich ganze Blöcke (BLOCKSIZE = 8192): pcmd = \ 'a2ps -q -%sB -l%s --borders=no --encoding=ibm-cp437 -o-' % ( self.a2ps, cpl) pfp = os.popen(pcmd, 'w') if pfp: while True: data = self.a2psfile.read(BUFSIZE) if not data: break data = data.decode('utf8').encode('cp850') pfp.write(data) else: err("failed to open a2ps pipe") Das funkt ja auch ganz gut, außer wenn jetzt dummerweise genau auf der Blockgrenze ein multibyte Zeichen liegt, geht das Ganze in die Hose: internal error: Traceback (most recent call last): File "/usr/bin/lpf", line 1421, in ? ret = lpf.run() File "/usr/bin/lpf", line 1309, in run self.lpr.close() File "/usr/bin/lpf", line 1007, in close data = data.decode('utf8').encode('cp850') File "/usr/lib/python2.4/encodings/utf_8.py", line 16, in decode return codecs.utf_8_decode(input, errors, True) UnicodeDecodeError: 'utf8' codec can't decode byte 0xe2 in position 8191: unexpected end of data So, wie die Sache steht, bleibt mir wohl nix anderes übrig, als auf zeilenweise Verarbeitung auszuweichen, oder hat jemand eine bessere Idee? Pete _______________________________________________ python-de maillist - python-de@python.net http://python.net/mailman/listinfo/python-de
So, wie die Sache steht, bleibt mir wohl nix anderes übrig, als auf zeilenweise Verarbeitung auszuweichen, oder hat jemand eine bessere Idee?
Ärgerliches Problem, ähnliches hatte ich auch schon mit DB-Spalten. Damals mit JAVA, dessen utf-encoder sowas dankenderweise überliest - die Daten waren dann zwar abgeschnitten, aber bei limitierten Spaltenbreiten passiert das eh. Kannst du nicht die Blöcke verketten? Dann könntest du im Fall eines Fehlers erstmal den nächsten anhängen. Es sollte doch schon _sehr_ mit dem teufel zugehen, wenn du da nicht früher oder später einen grösseren Block bekommst, der sich umkodieren lässt. Etwa so: blocks = [] while True: data = self.a2psfile.read(BUFSIZE) if not data: break blocks.append(data) try: out = "".join(blocks).decode('utf8').encode('cp850') pfp.write(data) blocks = [] except UnicodeDecodeError: pass MfG Diez _______________________________________________ python-de maillist - python-de@python.net http://python.net/mailman/listinfo/python-de
Diez B. Roggisch wrote:
Ärgerliches Problem, ähnliches hatte ich auch schon mit DB-Spalten. Damals mit JAVA, dessen utf-encoder sowas dankenderweise überliest - die Daten waren dann zwar abgeschnitten, aber bei limitierten Spaltenbreiten passiert das eh.
Ich sollte vielleicht noch nachtragen, das ich schon der Meinung bin das decdode-Fehler gut sind.... Nur war im konkreten Fall die Sonderbehandlung des letzten Zeichens dann überflüssig. Wofür ich dankbar war :) MfG Diez _______________________________________________ python-de maillist - python-de@python.net http://python.net/mailman/listinfo/python-de
Hi Diez, ich antworte mal an die Liste, so hat jeder was davon ;-) Am Samstag, 20. August 2005 12:30 schrieb Diez B. Roggisch:
So, wie die Sache steht, bleibt mir wohl nix anderes übrig, als auf zeilenweise Verarbeitung auszuweichen, oder hat jemand eine bessere Idee?
Ärgerliches Problem, ähnliches hatte ich auch schon mit DB-Spalten. Damals mit JAVA, dessen utf-encoder sowas dankenderweise überliest - die Daten waren dann zwar abgeschnitten, aber bei limitierten Spaltenbreiten passiert das eh.
Yup, und ich wette, diese Art von Problemen treten im asiatischen Sprachraum deutlich häufiger auf als bei uns..
Kannst du nicht die Blöcke verketten? Dann könntest du im Fall eines Fehlers erstmal den nächsten anhängen. Es sollte doch schon _sehr_ mit dem teufel zugehen, wenn du da nicht früher oder später einen grösseren Block bekommst, der sich umkodieren lässt.
Etwa so:
blocks = [] while True: data = self.a2psfile.read(BUFSIZE) if not data: break blocks.append(data) try: out = "".join(blocks).decode('utf8').encode('cp850') pfp.write(data) blocks = [] except UnicodeDecodeError: pass
Ahh, interessante Idee, aber ich versuche, den Speicherverbrauch möglichst deterministisch zu halten, sonst hätte ich mir den hassle mit dem Tempfile ganz sparen können, wenn im dümmsten Falle doch wieder alle Daten im Speicher landen. Alternativ könnte man die Blockgröße reduzieren, was dann zwar diese Wahrscheinlichkeit verringert, aber auch den Sinn und Zweck des blockweisen Verarbeitens in Frage stellt, also bleibt nach reiflicher Überlegung als beste Alternative doch nur die zeilenweise Verarbeitung (mit den Vorteilen der knappsten Notation und der Selbstdokumentiertheit ;-)). Aber immerhin war dieser Fehler nützlich, einen Problemaspekt im Umgang mit Multibyte-Encodings aufzuzeigen, der mir bis dahin noch nicht so bewußt war (manchmal schlagen da wohl noch meine Assembler und C Wurzeln durch). Danke Dir vielmals, Pete _______________________________________________ python-de maillist - python-de@python.net http://python.net/mailman/listinfo/python-de
Ahh, interessante Idee, aber ich versuche, den Speicherverbrauch möglichst deterministisch zu halten, sonst hätte ich mir den hassle mit dem Tempfile ganz sparen können, wenn im dümmsten Falle doch wieder alle Daten im Speicher landen. Alternativ könnte man die Blockgröße reduzieren, was dann zwar diese Wahrscheinlichkeit verringert, aber auch den Sinn und Zweck des blockweisen Verarbeitens in Frage stellt, also bleibt nach reiflicher Überlegung als beste Alternative doch nur die zeilenweise Verarbeitung (mit den Vorteilen der knappsten Notation und der Selbstdokumentiertheit ;-)).
Sind die Dateien so gross? Unabhängig davon kann man das ja auch nach oben begrenzen, indem man bei zwei Blöcken _und_ fehlerhafter Codierung den zweiten halbiert, und es dann nochmal probiert. Oder Zeichenweise zurückgeht. Den unparsierbaren Rest wieder nimmt mann dann als nächsten Block. Darüber hinaus denke ich das der worst-case _so_ unwahrscheinlich ist, das man das getrost ignoriren kann - es sei denn, du bist embedded "mit ohne" MMU oder so unterwegs. Mfg Diez _______________________________________________ python-de maillist - python-de@python.net http://python.net/mailman/listinfo/python-de
Hans-Peter Jansen wrote:
So, wie die Sache steht, bleibt mir wohl nix anderes übrig, als auf zeilenweise Verarbeitung auszuweichen, oder hat jemand eine bessere Idee?
In Python 2.4 kann der UTF-8-Decoder einen "partial mode": decoded = codecs.utf_8_decode(encoded, "strict", 0) Der letzte Parameter gibt an, ob es sich um das Ende der Eingabe handelt. Falls nicht, und falls am Ende Bytes fehlen, gibt er nur die dekodierten Zeichen und die Zahl der dazu konsumierten Bytes an. Man kann also schreiben rest = '' while True: data = self.a2psfile.read(BUFSIZE) if not data: pfp.write(rest.decode("utf-8").encode("cp850")) break decoded, len = codecs.utf_8_decode(rest + data, "strict", 0) rest = data[len:] pfp.write(decoded.encode("cp850")) In älteren Python-Versionen kann man das ineffizient nachbilden: def utf_8_decode(data, errors, final): if final: return data.decode("utf-8", errors), len(data) last = ord(data[-1]) if last < 0x80: # ASCII, always single-byte return data.decode("utf-8", errors), len(data) if last >= 0xc0: # last byte is lead byte return data[:-1].decode("utf-8", errors), len(data)-1 # find lead byte i = -2 while ord(data[i]) < 0xc0: i++ last = ord(data[i]) incomplete = False if last < 0xe0: # one extra byte, cannot be incomplete here pass elif last < 0xf0: # two extra bytes, incomplete if we have only one incomplete = (i != -3) else: # three extra bytes incomplete = (i != -4) if incomplete: return data[:i].decode("utf-8", errors), len(data)+i return data.decode("utf-8", errors) Achtung: Dieser Code ist komplett ungetestet. Ciao, Martin _______________________________________________ python-de maillist - python-de@python.net http://python.net/mailman/listinfo/python-de
Am Samstag, 20. August 2005 14:20 schrieb Martin v. Löwis:
Hans-Peter Jansen wrote:
So, wie die Sache steht, bleibt mir wohl nix anderes übrig, als auf zeilenweise Verarbeitung auszuweichen, oder hat jemand eine bessere Idee?
In Python 2.4 kann der UTF-8-Decoder einen "partial mode":
decoded = codecs.utf_8_decode(encoded, "strict", 0)
Der letzte Parameter gibt an, ob es sich um das Ende der Eingabe handelt. Falls nicht, und falls am Ende Bytes fehlen, gibt er nur die dekodierten Zeichen und die Zahl der dazu konsumierten Bytes an.
Man kann also schreiben
rest = '' while True: data = self.a2psfile.read(BUFSIZE) if not data: pfp.write(rest.decode("utf-8").encode("cp850")) break decoded, len = codecs.utf_8_decode(rest + data, "strict", 0) rest = data[len:] pfp.write(decoded.encode("cp850"))
Genau an sowas dachte ich, als ich mit dem Problem konfrontiert war. Gut zu wissen, auch wenn es erstmal nur für 2.4 geht.
In älteren Python-Versionen kann man das ineffizient nachbilden:
def utf_8_decode(data, errors, final): if final: return data.decode("utf-8", errors), len(data) last = ord(data[-1]) if last < 0x80: # ASCII, always single-byte return data.decode("utf-8", errors), len(data) if last >= 0xc0: # last byte is lead byte return data[:-1].decode("utf-8", errors), len(data)-1 # find lead byte i = -2 while ord(data[i]) < 0xc0: i++ last = ord(data[i]) incomplete = False if last < 0xe0: # one extra byte, cannot be incomplete here pass elif last < 0xf0: # two extra bytes, incomplete if we have only one incomplete = (i != -3) else: # three extra bytes incomplete = (i != -4) if incomplete: return data[:i].decode("utf-8", errors), len(data)+i return data.decode("utf-8", errors)
Achtung: Dieser Code ist komplett ungetestet.
..und viel zu komplex, um in meinem Falle einen Einsatz zu rechtfertigen. Aber dennoch sehr nützlich, um mehr über die Struktur der utf8 Kodierung zu erfahren. So komprimiert habe ich das noch nirgends gefunden. Cool, danke Martin. Das lege heute nacht unter mein Kopfkissen ;-) Pete _______________________________________________ python-de maillist - python-de@python.net http://python.net/mailman/listinfo/python-de
Hans-Peter Jansen wrote:
..und viel zu komplex, um in meinem Falle einen Einsatz zu rechtfertigen. Aber dennoch sehr nützlich, um mehr über die Struktur der utf8 Kodierung zu erfahren. So komprimiert habe ich das noch nirgends gefunden.
Das wird halt über UTF-8 immer gelobt: Man kann jedem Byte ansehen, ob es - ein einzelnes Byte ist - das erste byte einer multi-byte-Folge, und falls ja, wieviele Folgebytes kommen - ein Folgebyte Da bedeutet, man kann "mittendrin" einsteigen und sich wieder leicht synchronisieren, oder, wie in diesem Fall, auch "rückwärts" lesen. Ich hatte allerdings bisher geglaubt, dass diese tollen Eigenschaften in der Praxis nie eine Rolle spielen. Ciao, Martin _______________________________________________ python-de maillist - python-de@python.net http://python.net/mailman/listinfo/python-de
participants (3)
-
"Martin v. Löwis"
-
Diez B. Roggisch
-
Hans-Peter Jansen