Als ich heute einem Kollegen beim Studium eines unerklärlichen UnicodeDecodeErrors half, entdeckte ich eine kleine Falle in der Python-String-Formatierung, die ich hier aufzeichnen werde. Der ursprüngliche Code war zu kompliziert und hatte zu viele Dinge, die nichts mit dem Problem zu tun hatten, also habe ich das Problem durch einen einfachen Test in ipython reproduziert. Der Prozess ist wie folgt:
In [4]: a = '你好世界' In [5]: print 'Say this: %s' % a Say this: 你好世界 In [6]: print 'Say this: %s and say that: %s' % (a, 'hello world') Say this: 你好世界 and say that: hello world In [7]: print 'Say this: %s and say that: %s' % (a, u'hello world') --------------------------------------------------------------------------- UnicodeDecodeError Traceback (most recent call last) /home/jerry/ in () UnicodeDecodeError: 'ascii' codec can't decode byte 0xe4 in position 10: ordinal not in range(128) In [8]: a Out[8]: '\xe4\xbd\xa0\xe5\xa5\xbd\xe4\xb8\x96\xe7\x95\x8c'
Der folgende In [7] ist UnicodeDecodeError? Der einzige Unterschied zum vorherigen Satz besteht darin, dass „hello world“ zu einem Unicode-Objekt anstelle eines str-Objekts wird. Das Problem ist jedoch, dass „Hallo Welt“ nur eine einfache englische Zeichenfolge ist, die keine anderen Zeichen als ASCII enthält. Wie kann sie nicht dekodiert werden? Schauen Sie sich die der Ausnahme beigefügte Nachricht genauer an. Sie erwähnt 0xe4. Dies ist offensichtlich nicht in „Hallo Welt“, daher können wir nur an der ausgedruckten Bytesequenz zweifeln sei es. Das erste ist 0xe4.
Es scheint, dass Python beim Formatieren der Zeichenfolge versucht, a in ein Unicode-Objekt zu dekodieren, und beim Dekodieren wird die Standard-ASCII-Kodierung anstelle der eigentlichen UTF-8-Kodierung verwendet. Was ist denn los? ? Lassen Sie uns unser Experiment fortsetzen:
In [9]: 'Say this: %s' % 'hello' Out[9]: 'Say this: hello' In [10]: 'Say this: %s' % u'hello' Out[10]: u'Say this: hello'
Schauen Sie genau hin, „Hallo“ in In [9] ist eine gewöhnliche Zeichenfolge, und das Ergebnis ist auch eine Zeichenfolge (Str-Objekt), während „in In [10] Hallo ist.“ ' wird zu einem Unicode-Objekt und das formatierte Ergebnis wird ebenfalls zu Unicode (beachten Sie das u am Anfang des Ergebnisses).
Die Wahrheit ist also: Python hat einige versteckte Tricks beim Formatieren von Zeichenfolgen: Wenn der Parameter, der %s entspricht, Unicode enthält, ist das Endergebnis ebenfalls Unicode. In diesem Fall werden die Vorlagenzeichenfolge und alle Zeichenfolgen im Parameter %s in Unicode dekodiert. Diese Dekodierung ist jedoch implizit und der Benutzer kann den verwendeten Zeichensatz nicht angeben. Python kann nur den Standard-ASCII verwenden. Wenn darin zufällig eine nicht ASCII-codierte Zeichenfolge enthalten ist, ist es vorbei...
Schauen Sie sich an, was in der Python-Dokumentation steht:
If format is a Unicode object, or if any of the objects being converted using the %s conversion are Unicode objects, the result will also be a Unicode object.
Wenn der Code str und Unicode mischt, Solche Probleme können leicht auftreten. Im Code meines Kollegen wurde die chinesische Zeichenfolge vom Benutzer eingegeben und korrekt codiert. Es handelt sich um ein in UTF-8 codiertes STR-Objekt. Obwohl sein Inhalt ausschließlich aus ASCII-Codes besteht, ist seine Quelle das Ergebnis Die SQLite3-Datenbankabfrage und die von der SQLite-API zurückgegebenen Zeichenfolgen sind alle Unicode-Objekte, was zu solch seltsamen Ergebnissen führt.
Str und Unicode von Python 2 schummeln wirklich, und ich wurde dadurch mehrmals geschädigt. Python 3 hat in dieser Hinsicht große Verbesserungen gebracht und ich freue mich auf seine volle Popularität!