Python str vs Unicode-Typen

101

Bei der Arbeit mit Python 2.7 frage ich mich, welchen wirklichen Vorteil die Verwendung des Typs unicodeanstelle von hat str, da beide anscheinend Unicode-Zeichenfolgen enthalten können. Gibt es einen besonderen Grund, außer Unicode-Codes in unicodeStrings mit dem Escape- Zeichen setzen zu können \?:

Ausführen eines Moduls mit:

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

a = 'á'
ua = u'á'
print a, ua

Ergebnisse in: á, á

BEARBEITEN:

Weitere Tests mit der Python-Shell:

>>> a = 'á'
>>> a
'\xc3\xa1'
>>> ua = u'á'
>>> ua
u'\xe1'
>>> ua.encode('utf8')
'\xc3\xa1'
>>> ua.encode('latin1')
'\xe1'
>>> ua
u'\xe1'

Die unicodeZeichenfolge scheint also mit latin1anstatt codiert zu sein, utf-8und die Rohzeichenfolge wird mit utf-8? Codiert . Ich bin jetzt noch verwirrter! : S.

Caumons
quelle
Es gibt keine Codierung für unicode, es ist nur eine Abstraktion von Unicode-Zeichen; unicodekann strmit etwas Codierung (zB utf-8) konvertiert werden .
Bin

Antworten:

178

unicodeist für den Umgang mit Text gedacht . Text ist eine Folge von Codepunkten, die größer als ein einzelnes Byte sein können . Text kann codiert in einer bestimmten Codierung den Text als unformatierten Bytes darzustellen (z utf-8, latin-1...).

Beachten Sie, dass unicode nicht codiert ist ! Die von Python verwendete interne Darstellung ist ein Implementierungsdetail, und Sie sollten sich nicht darum kümmern, solange sie die gewünschten Codepunkte darstellen kann.

Im Gegensatz dazu ist strin Python 2 eine einfache Folge von Bytes . Es stellt keinen Text dar!

Sie können sich unicodeeine allgemeine Darstellung eines Textes vorstellen, der auf viele verschiedene Arten in eine Folge von Binärdaten codiert werden kann, die über dargestellt werden str.

Hinweis: In Python 3 unicodewurde in umbenannt strund es gibt einen neuen bytesTyp für eine einfache Folge von Bytes.

Einige Unterschiede, die Sie sehen können:

>>> len(u'à')  # a single code point
1
>>> len('à')   # by default utf-8 -> takes two bytes
2
>>> len(u'à'.encode('utf-8'))
2
>>> len(u'à'.encode('latin1'))  # in latin1 it takes one byte
1
>>> print u'à'.encode('utf-8')  # terminal encoding is utf-8
à
>>> print u'à'.encode('latin1') # it cannot understand the latin1 byte

Beachten Sie, dass strSie bei der Verwendung ein untergeordnetes Steuerelement für die einzelnen Bytes einer bestimmten Codierungsdarstellung haben, während unicodeSie die Steuerung nur auf Codepunktebene steuern können. Zum Beispiel können Sie Folgendes tun:

>>> 'àèìòù'
'\xc3\xa0\xc3\xa8\xc3\xac\xc3\xb2\xc3\xb9'
>>> print 'àèìòù'.replace('\xa8', '')
à�ìòù

Was vorher gültig war UTF-8, ist nicht mehr. Mit einer Unicode-Zeichenfolge können Sie nicht so arbeiten, dass die resultierende Zeichenfolge keinen gültigen Unicode-Text enthält. Sie können einen Codepunkt entfernen, einen Codepunkt durch einen anderen Codepunkt ersetzen usw., aber Sie können nicht mit der internen Darstellung herumspielen.

Bakuriu
quelle
4
Vielen Dank für Ihre Antwort, es hat sehr geholfen! Der klarste Teil für mich ist: "Unicode ist nicht codiert! Die von Python verwendete interne Darstellung ist ein Implementierungsdetail, und Sie sollten sich nicht darum kümmern [...]". Wenn unicodewir Objekte serialisieren , müssen wir sie vermutlich zuerst explizit encode()in das richtige Codierungsformat bringen, da wir nicht wissen, welches intern zur Darstellung des unicodeWerts verwendet wird.
Caumons
10
Ja. Wenn Sie Text speichern möchten (z. B. in einer Datei), müssen Sie ihn mit Bytes darstellen, dh Sie müssen ihn codieren . Beim Abrufen des Inhalts sollten Sie die verwendete Codierung kennen, um die Bytes in ein unicodeObjekt dekodieren zu können .
Bakuriu
Es tut mir leid, aber die Aussage, unicodedie nicht verschlüsselt ist, ist einfach falsch. UTF-16 / UCS-2 und UTF-32 / UCS-4 sind ebenfalls Codierungen ... und in Zukunft werden möglicherweise weitere davon erstellt. Nur weil Sie sich nicht um die Implementierungsdetails kümmern sollten (und das sollten Sie auch nicht!), Bedeutet dies nicht, dass diese unicodenicht codiert sind. Das ist es natürlich. Ob es möglich ist .decode(), ist eine ganz andere Geschichte.
0xC0000022L
1
@ 0xC0000022L Vielleicht ist der Satz so wie er ist unklar. Es sollte heißen: Die unicodeinterne Darstellung des Objekts kann beliebig sein, einschließlich einer nicht standardmäßigen. Insbesondere in Python3 + unicode wird eine nicht standardmäßige interne Darstellung verwendet, die sich auch in Abhängigkeit von den enthaltenen Daten ändert. Als solches ist es keine Standardcodierung . Unicode als Textstandard definiert nur Codepunkte, die eine abstrakte Darstellung von Text darstellen. Es gibt unzählige Möglichkeiten, Unicode im Speicher zu codieren, einschließlich des Standards utf-X usw. Python verwendet seinen eigenen Weg für die Effizienz.
Bakuriu
1
@ 0xC0000022L Auch die Tatsache, dass UTF-16 eine Codierung ist, hat nichts mit dem CPython- unicodeObjekt zu tun , da weder UTF-16 noch UTF-32 verwendet werden. Es wird eine Ad-hoc-Darstellung verwendet. Wenn Sie die Daten in tatsächliche Bytes codieren möchten, müssen Sie sie verwenden encode. Außerdem: Die Sprache schreibt nicht vor, wie unicodeimplementiert werden soll, sodass unterschiedliche Versionen oder Implementierungen von Python unterschiedliche interne Darstellungen haben können (und haben ).
Bakuriu
38

Unicode und Codierungen sind völlig unterschiedliche, nicht miteinander verbundene Dinge.

Unicode

Weist jedem Zeichen eine numerische ID zu:

  • 0x41 → A.
  • 0xE1 → á
  • 0x414 → Д

Daher weist Unicode A die Nummer 0x41, á 0xE1 und Д 0x414 zu.

Sogar der kleine Pfeil →, den ich verwendet habe, hat seine Unicode-Nummer, es ist 0x2192. Und selbst Emojis haben ihre Unicode-Nummern, 😂 ist 0x1F602.

Sie können die Unicode-Nummern aller Zeichen in dieser Tabelle nachschlagen . Insbesondere die ersten drei Zeichen oben findet hier , Pfeil die hier und die Emojis hier .

Diese von Unicode allen Zeichen zugewiesenen Nummern werden als Codepunkte bezeichnet .

Der Zweck all dessen ist es, ein Mittel bereitzustellen, um sich eindeutig auf jedes Zeichen zu beziehen. Wenn ich zum Beispiel über 😂 spreche, anstatt zu sagen "Sie wissen, dieses lachende Emoji mit Tränen" , kann ich einfach sagen: Unicode-Codepunkt 0x1F602 . Einfacher, oder?

Beachten Sie, dass Unicode-Codepunkte normalerweise mit einem führenden Format formatiert U+werden und der hexadezimale numerische Wert auf mindestens 4 Stellen aufgefüllt wird. Die obigen Beispiele wären also U + 0041, U + 00E1, U + 0414, U + 2192, U + 1F602.

Unicode-Codepunkte reichen von U + 0000 bis U + 10FFFF. Das sind 1.114.112 Zahlen. 2048 dieser Zahlen für verwendet werden Surrogate , so verbleiben 1.112.064. Dies bedeutet, dass Unicode 1.112.064 verschiedenen Zeichen eine eindeutige ID (Codepunkt) zuweisen kann. Noch sind nicht alle diese Codepunkte einem Zeichen zugeordnet, und Unicode wird kontinuierlich erweitert (z. B. wenn neue Emojis eingeführt werden).

Es ist wichtig, sich daran zu erinnern, dass Unicode lediglich jedem Zeichen eine numerische ID (Codepunkt) zuweist, um eine einfache und eindeutige Bezugnahme zu ermöglichen.

Kodierungen

Ordnen Sie Zeichen Bitmustern zu.

Diese Bitmuster werden verwendet, um die Zeichen im Computerspeicher oder auf der Festplatte darzustellen.

Es gibt viele verschiedene Codierungen, die verschiedene Teilmengen von Zeichen abdecken. Im englischsprachigen Raum sind die häufigsten Codierungen die folgenden:

ASCII

Ordnet 128 Zeichen (Codepunkte U + 0000 bis U + 007F) Bitmustern der Länge 7 zu.

Beispiel:

  • a → 1100001 (0x61)

Sie können alle Zuordnungen in dieser Tabelle sehen .

ISO 8859-1 (auch bekannt als Latin-1)

Ordnet 191 Zeichen (Codepunkte U + 0020 bis U + 007E und U + 00A0 bis U + 00FF) Bitmustern der Länge 8 zu.

Beispiel:

  • a → 01100001 (0x61)
  • → 11100001 (0xE1)

Sie können alle Zuordnungen in dieser Tabelle sehen .

UTF-8

Karten 1.112.064 Zeichen (alle Unicode vorhandene Code Punkte) Bitmustern beiden Längen 8, 16, 24 oder 32 Bits (das heißt, 1, 2, 3 oder 4 Bytes).

Beispiel:

  • a → 01100001 (0x61)
  • → 11000011 10100001 (0xC3 0xA1)
  • → 11100010 10001001 10100000 (0xE2 0x89 0xA0)
  • → 11110000 10011111 10011000 10000010 (0xF0 0x9F 0x98 0x82)

Die Art und Weise UTF-8 kodiert Zeichen Bitfolgen ist sehr gut beschrieben hier .

Unicode und Codierungen

Anhand der obigen Beispiele wird deutlich, wie nützlich Unicode ist.

Wenn ich zum Beispiel Latin-1 bin und meine Kodierung von á erklären möchte, muss ich nicht sagen:

"Ich codiere das a mit einem Aigu (oder wie auch immer Sie diesen ansteigenden Balken nennen) als 11100001"

Aber ich kann nur sagen:

"Ich codiere U + 00E1 als 11100001"

Und wenn ich UTF-8 bin, kann ich sagen:

"Ich wiederum codiere U + 00E1 als 11000011 10100001"

Und jedem ist eindeutig klar, welchen Charakter wir meinen.

Nun zu der oft auftretenden Verwirrung

Es ist wahr, dass manchmal das Bitmuster einer Codierung, wenn Sie es als Binärzahl interpretieren, mit dem Unicode-Codepunkt dieses Zeichens identisch ist.

Beispielsweise:

  • ASCII codiert a als 1100001, was Sie als Hexadezimalzahl 0x61 interpretieren können , und der Unicode-Codepunkt von a ist U + 0061 .
  • Latin-1 codiert á als 11100001, was Sie als Hexadezimalzahl 0xE1 interpretieren können , und der Unicode-Codepunkt von á ist U + 00E1 .

Natürlich wurde dies aus Bequemlichkeitsgründen absichtlich so angeordnet. Aber Sie sollten es als reinen Zufall betrachten . Das Bitmuster, das zur Darstellung eines Zeichens im Speicher verwendet wird, ist in keiner Weise an den Unicode-Codepunkt dieses Zeichens gebunden.

Niemand sagt, dass Sie eine Bitfolge wie 11100001 als Binärzahl interpretieren müssen. Betrachten Sie es einfach als die Folge von Bits, mit denen Latin-1 das Zeichen á codiert .

Zurück zu Ihrer Frage

Die von Ihrem Python-Interpreter verwendete Codierung ist UTF-8 .

In Ihren Beispielen ist Folgendes vor sich:

Beispiel 1

Im Folgenden wird das Zeichen á in UTF-8 codiert. Daraus ergibt sich die Bitfolge 11000011 10100001, die in der Variablen gespeichert wird a.

>>> a = 'á'

Wenn Sie sich den Wert von ansehen a, wird sein Inhalt 11000011 10100001 als Hexadezimalzahl 0xC3 0xA1 formatiert und wie folgt ausgegeben '\xc3\xa1':

>>> a
'\xc3\xa1'

Beispiel 2

Im Folgenden wird der Unicode-Codepunkt von á, der U + 00E1 ist, in der Variablen uagespeichert (wir wissen nicht, welches Datenformat Python intern verwendet, um den Codepunkt U + 00E1 im Speicher darzustellen, und es ist für uns unwichtig):

>>> ua = u'á'

Wenn Sie sich den Wert von ansehen ua, sagt Python, dass er den Codepunkt U + 00E1 enthält:

>>> ua
u'\xe1'

Beispiel 3

Das Folgende codiert den Unicode-Codepunkt U + 00E1 (der das Zeichen á darstellt) mit UTF-8, was zu dem Bitmuster 11000011 10100001 führt. Wiederum wird dieses Bitmuster für die Ausgabe als Hexadezimalzahl 0xC3 0xA1 dargestellt:

>>> ua.encode('utf-8')
'\xc3\xa1'

Beispiel 4

Das Folgende codiert den Unicode-Codepunkt U + 00E1 (der das Zeichen á darstellt) mit Latin-1, was zum Bitmuster 11100001 führt. Für die Ausgabe wird dieses Bitmuster als Hexadezimalzahl 0xE1 dargestellt, die zufällig mit der Initiale identisch ist Codepunkt U + 00E1:

>>> ua.encode('latin1')
'\xe1'

Es gibt keine Beziehung zwischen dem Unicode-Objekt uaund der Latin-1-Codierung. Dass der Codepunkt von á U + 00E1 ist und die Latin-1-Codierung von á 0xE1 ist (wenn Sie das Bitmuster der Codierung als Binärzahl interpretieren), ist ein reiner Zufall.

weibeld
quelle
31

Ihr Terminal ist zufällig auf UTF-8 konfiguriert.

Die Tatsache, dass der Druck afunktioniert, ist ein Zufall; Sie schreiben rohe UTF-8-Bytes in das Terminal. aist ein Wert der Länge zwei , der zwei Bytes enthält, Hex-Werte C3 und A1, während uaein Unicode-Wert der Länge eins ist , der einen Codepunkt U + 00E1 enthält.

Dieser Längenunterschied ist ein Hauptgrund für die Verwendung von Unicode-Werten. Sie können die Anzahl der Textzeichen in einer Byte-Zeichenfolge nicht einfach messen . Die len()Zeichenfolge gibt an, wie viele Bytes verwendet wurden und nicht, wie viele Zeichen codiert wurden.

Sie können den Unterschied sehen , wenn Sie kodieren den Unicode - Wert für verschiedene Ausgabecodierung:

>>> a = 'á'
>>> ua = u'á'
>>> ua.encode('utf8')
'\xc3\xa1'
>>> ua.encode('latin1')
'\xe1'
>>> a
'\xc3\xa1'

Beachten Sie, dass die ersten 256 Codepunkte des Unicode-Standards mit dem Latin 1-Standard übereinstimmen, sodass der U + 00E1-Codepunkt als Byte mit dem Hex-Wert E1 in Latin 1 codiert wird.

Darüber hinaus verwendet Python Escape-Codes in Darstellungen von Unicode- und Byte-Zeichenfolgen, und niedrige Codepunkte, die nicht als ASCII gedruckt werden können, werden ebenfalls mit \x..Escape-Werten dargestellt. Aus diesem Grund sieht eine Unicode-Zeichenfolge mit einem Codepunkt zwischen 128 und 255 genauso aus wie die Latin 1-Codierung. Wenn Sie eine Unicode-Zeichenfolge mit Codepunkten jenseits von U + 00FF haben, \u....wird stattdessen eine andere Escape-Sequenz mit einem vierstelligen Hex-Wert verwendet.

Es sieht so aus, als ob Sie den Unterschied zwischen Unicode und einer Codierung noch nicht vollständig verstehen. Bitte lesen Sie die folgenden Artikel, bevor Sie fortfahren:

Martijn Pieters
quelle
Ich habe meine Frage mit weiteren Tests bearbeitet. Ich habe eine Weile nach Unicode und den verschiedenen Codierungen gelesen und ich glaube, ich verstehe die Theorie, aber wenn ich Python-Code tatsächlich
teste,
1
Die Latin-1-Codierung entspricht den ersten 256 Codepunkten des Unicode-Standards. Aus diesem Grund codiert U + 00E1 \xe1in Latein 1.
Martijn Pieters
2
Dies ist der wichtigste Aspekt von Unicode. Es ist keine Kodierung . Es ist Text. Unicode ist ein Standard, der viel, viel mehr enthält, wie Informationen darüber, welche Codepunkte Zahlen oder Leerzeichen oder andere Kategorien sind, von links nach rechts oder von rechts nach links usw. usw. usw. angezeigt werden sollten
Martijn Pieters
1
Es ist so, als würde man sagen, Unicode sei wie eine "Schnittstelle" und Codierung sei wie eine tatsächliche "Implementierung".
Caumons
2
@Varun: Sie müssen einen engen Python 2-Build verwenden, der UCS-2 intern verwendet und alles über U + FFFF als Länge zwei falsch darstellt. Python 3 und ein UCS-2 (breit) Build zeigen Ihnen, dass die Länge wirklich 1 ist.
Martijn Pieters
2

Wenn Sie a als Unicode definieren, sind die Zeichen a und á gleich. Ansonsten zählt á als zwei Zeichen. Versuchen Sie es mit len ​​(a) und len (au). Darüber hinaus benötigen Sie möglicherweise die Codierung, wenn Sie mit anderen Umgebungen arbeiten. Wenn Sie beispielsweise md5 verwenden, erhalten Sie unterschiedliche Werte für a und ua

Ali Rasim Kocal
quelle