Wie werden Zeichen dargestellt -- ASCII und Unicode
In dieser Vorlesung werden Sie verstehen: wie Text in einem Computer dargestellt wird, der nur Zahlen speichern kann, und warum das Öffnen einer Datei manchmal unlesbaren Text erzeugt.
Alltagsanalogie: Zimmernummerierung
Stellen Sie sich ein Gebäude mit 128 Zimmern vor.
Die Rezeption verwaltet Zimmer nicht nach Namen wie "Präsidentensuite" oder "Gartenzimmer".
Sie weisen jedem Zimmer eine Nummer zu: 101, 102, 103...
Schlagen Sie Nummer 101 nach, und Sie wissen: Oh, das ist ein Standard-King-Zimmer.
Computer verarbeiten Text auf genau dieselbe Weise:
Weisen Sie jedem Zeicheneinen numerischen Code zu, dann speichern und verarbeiten Sie diese Zahl.
Dieses "Nummerierungsschema" ist Zeichenkodierung.
ASCII: Der Ursprung aller Kodierungen
ASCII (American Standard Code for Information Interchange, United States Information Interchange Standard Code) entstand im Jahr 1963.
Es verwendet 7 Bits, um ein Zeichen darzustellen, insgesamt 2^7 = 128 Zeichen.
Enthält:
- Steuerzeichen (0~31): unsichtbare Zeichen wie Wagenrücklauf, Zeilenvorschub und Rücktaste
- Druckbare Zeichen (32~126): Leerzeichen, Satzzeichen, Ziffern 0-9, Großbuchstaben A-Z, Kleinbuchstaben a-z
- DEL Löschzeichen (127)
Vollständige ASCII-Tabelle (0~127)

Wichtige Ankerpunkte in ASCII
| Zeichenkategorie | ASCII-Bereich | Merkmal |
|---|---|---|
| '0' ~ '9' | 48 ~ 57 (0x30 ~ 0x39) | Der Code für die Ziffer 0 ist 48, nicht 0. |
| 'A' ~ 'Z' | 65 ~ 90 (0x41 ~ 0x5A) | Großbuchstaben sind fortlaufend angeordnet |
| 'a' ~ 'z' | 97 ~ 122 (0x61 ~ 0x7A) | Kleinbuchstaben sind fortlaufend angeordnet |
| Zeilenumbruch '\n' | 10 (0x0A) | LF, Unix/Mac-Zeilenumbruch |
| Wagenrücklauf '\r' | 13 (0x0D) | CR, der Zeilenumbruch, den alte Macs verwendeten |
Der clevere Zusammenhang zwischen Groß- und Kleinbuchstaben
Beachten Sie die Binärdarstellung von Großbuchstabe 'A' (65) und Kleinbuchstabe 'a' (97):
'a' = 01100001
Nur Bit 5 unterscheidet sich (Wert = 32, Bitnummerierung von rechts nach links, beginnend bei 0)
Das ist kein Zufall.
Auf der Binärebene bewirkt das Ändern von Bit 5 von 0 auf 1 direkt die Umwandlung von Groß- in Kleinbuchstaben.
Dieses clevere Design ermöglichte frühen Fernschreibern, mit nur einer einfachen Schaltung zwischen Groß- und Kleinbuchstabenmodus umzuschalten.
Bit 5 wird nach der Konvention gezählt, Bits von rechts nach links beginnend bei 0 zu nummerieren (d. h. das LSB ist Bit 0), was die gebräuchlichste Bitnummerierungskonvention in Computern ist:

Groß- und Kleinbuchstaben unterscheiden sich um 32, also auf ASCII-Ebene,
'a' - 'A' = 32,'A' | 0x20 = 'a'(Bitweise Operation zum Umschalten der Groß-/Kleinschreibung). All dies rührt von der cleveren Anordnung der ASCII-Kodierungstabelle her.
Die Einschränkung von ASCII: Ein Byte reicht nicht aus
ASCII hat nur 128 Zeichen, was für Englisch ausreicht.
Doch als sich Computer in Europa ausbreiteten, entstanden Probleme:
Französisch hat é, Deutsch hat ü, Spanisch hat ñ – diese akzentuierten Buchstaben existieren in ASCII schlicht nicht.
Also begann jedes Land zu „patchen“—und nutzte den Bereich 128~255, den ASCII nicht verwendete (mit dem 8. Bit auf 1 gesetzt):
- Westeuropa: ISO-8859-1 (Latin-1), womit westeuropäische Zeichen wie e und u hinzugefügt wurden
- Osteuropa: ISO-8859-2, fügte slawische Buchstaben hinzu
- Griechenland: ISO-8859-7, fügte griechische Buchstaben hinzu
- China: GB2312/GBK, verwendet zwei Bytes, um ein chinesisches Schriftzeichen darzustellen
- Japan: Shift-JIS
- Korea: EUC-KR
Das wurde zu einer Katastrophe—Dieselben Binärdaten ergeben, wenn sie unter verschiedenen Kodierungen interpretiert werden, völlig unterschiedlichen Text。
Öffnest du ein japanisches Dokument und siehst Zeichensalat? Das ist das klassische Symptom einer Kodierungs-Nichtübereinstimmung.
Unicode: jedem Zeichen der Welt eine eindeutige Nummer geben
Das Ziel von Unicode ist einfach und doch ehrgeizig:
Jedem Zeichen der Welt einen eindeutigen numerischen Code (Code Point) zuweisen.。
Unicode ist nicht 16-Bit – sein Codepunktbereich reicht von U+0000 bis U+10FFFF und kann mehr als 1 Million Zeichen umfassen.
Bisher wurden etwa 150,000 Zeichen zugewiesen, die nahezu alle menschlichen Schriftsysteme sowie Emoji abdecken.
Beispiele für Unicode-Codepunkte
| Zeichen | Unicode-Codepunkt | Bedeutung | Zugewiesener Block |
|---|---|---|---|
| A | U+0041 | Lateinischer Großbuchstabe A | Grundlegendes lateinisches Alphabet (ASCII-kompatibel) |
| du | U+4F60 | CJK-chinesisches Schriftzeichen「你」 | Vereinheitlichte CJK-Ideogramme |
| Gut | U+597D | CJK-chinesisches Schriftzeichen「好」 | Vereinheitlichte CJK-Ideogramme |
| hallo | U+68D2 | Arabisch „Hallo“ | Arabisch |
| example | U+E0001 | Sprach-Tag | Tags-Block |
Hinweis: Unicode ist nur ein „Nummerierungsschema“ – es sagt dir: „Zeichen X hat die Nummer U+XXXX“.
Es legt nicht fest, „wie die Nummer U+XXXX in einer Datei gespeichert wird“.
Das Speichern in Dateien ist Aufgabe von „Kodierungsschemata“ wie UTF-8 und UTF-16.
Viele Menschen verwechseln Unicode mit UTF-8. Merken Sie sich diesen Unterschied: Unicode ist ein Wörterbuch (das definiert, welches Zeichen welcher Nummer entspricht), während UTF-8 eine Schreibregel ist (die festlegt, wie eine Nummer als Bytefolge geschrieben wird).
UTF-8: das beliebteste Unicode-Kodierungsschema
Das Design von UTF-8 ist äußerst clever. Es ist eineKodierung mit variabler Länge:
- ASCII-Zeichen (U+0000 ~ U+007F): in 1 Byte gespeichert
- Die meisten europäischen und arabischen Zeichen: verwenden 2 Bytes
- Chinesisch, Japanisch und Koreanisch: 3 Bytes verwenden
- Ergänzende Zeichen (einschließlich der meisten Emojis): 4 Bytes verwenden
UTF-8-Kodierungsregeln
Vom ersten Byteführendes BitmusterEs teilt dem Decoder mit: wie viele Bytes dieses Zeichen belegt.
- Beginnt mit 0: Einzelbyte-Zeichen
- Beginnt mit 110: Zwei-Byte-Zeichen
- Beginnt mit 1110: Drei-Byte-Zeichen
- Beginnt mit 11110: Vier-Byte-Zeichen
- Nachfolgende Bytes beginnen alle mit 10
Der größte Vorteil von UTF-8:Vollständig kompatibel mit ASCII。
Jeder gültige ASCII-Text ist auch gültiger UTF-8-Text.
Diese Eigenschaft ermöglicht es UTF-8, das alte ASCII-System reibungslos zu ersetzen, ohne vorhandene Daten zu beschädigen.
Interaktive Demo: Echtzeit-Kodierung nachschlagen
Geben Sie unten einen beliebigen Text ein, um den Unicode-Codepunkt jedes Zeichens, die UTF-8-kodierten Bytes und den belegten Speicherplatz anzuzeigen.
Bedienungshinweise:
- Geben Sie rein englischen Text ein und beobachten Sie, dass jedes Zeichen nur 1 Byte belegt.
- Geben Sie chinesischen Text ein und beobachten Sie, dass jedes chinesische Zeichen 3 Bytes belegt.
- Eingabe
Hello EXAMPLEBeachten Sie, ob die ASCII-Codes der Buchstaben aufeinanderfolgend angeordnet sind. - Vergleiche die Binärdarstellungen von Groß- und Kleinbuchstaben und finde die entsprechenden Buchstabenpaare, die sich nur um 1 Bit unterscheiden (wie A und a).
Problem der Kodierungsverstümmelung: Grundlagen und Vermeidung
Das Wesen von Zeichensalat lässt sich in einem Satz zusammenfassen:Daten mit dem falschen Wörterbuch interpretieren。
Angenommen, eine Datei enthält drei Bytes, die in UTF-8-Kodierung gespeichert sind:E4 BD A0
- Mit UTF-8 dekodieren: Diese 3 Bytes bilden ein Zeichen → "du"
- Mit GBK dekodieren: E4 BD wird als GBK-Zweibyte-Zeichen behandelt → könnte irgendein fremdes chinesisches Zeichen sein
- Mit Latin-1 dekodieren: E4, BD, A0 werden als drei unabhängige westeuropäische Zeichen behandelt → Zeichensalat wie "a + 1/2 + nbsp"
Dieselben Binärdaten, mit drei verschiedenen Wörterbüchern dekodiert, ergeben drei verschiedene Texte.
Wie vermeidet man Zeichensalat?
- UTF-8 einheitlich verwendenDas moderne Web, APIs und Datenbanken verwenden fast alle standardmäßig UTF-8
- Kodierung explizit deklarieren: In HTML schreiben
<meta charset="UTF-8">, in den HTTP-Antwortheader schreibenContent-Type: text/html; charset=utf-8 - Editor-EinstellungenEditoren wie VS Code und Sublime unterstützen alle das Festlegen der Dateikodierung auf UTF-8
Problem mit der BOM (Byte Order Mark)
Obwohl UTF-8 sich nicht um die Byte-Reihenfolge kümmern muss (da es bytebasiert ist), fügen einige Windows-Programme am Anfang von UTF-8-Dateien eine BOM hinzu:EF BB BF。
Diese drei Bytes werden verwendet, um zu markieren, dass „diese Datei UTF-8 ist“.
Auf Unix/Linux-Systemen kann die BOM jedoch verschiedene Probleme verursachen:
- Die erste Zeile eines Shell-Skripts
#!/bin/bashDie drei zusätzlichen unsichtbaren Bytes am Anfang führen dazu, dass das Skript nicht ausgeführt werden kann - Wenn eine PHP-Datei eine BOM enthält, kann dies einen Fehler „headers already sent“ auslösen
- JSON-Parser können JSON-Daten ablehnen, die mit einer BOM beginnen
Empfehlung: Verwenden Sie das Format „no BOM“ für UTF-8-Dateien (UTF-8 ohne BOM), das auch die Standardeinstellung der meisten modernen Editoren ist.
Code-Demonstration: Kodierung und Dekodierung
Beispiel
import sys
# =============================================
# Demo 1: Grundlegende Verwendung von ord() und chr()
# =============================================
print("=" * 60)
print("Demo 1: Umwandlung zwischen Zeichen und Codepunkten")
print("=" * 60)
# ord() ermittelt den Unicode-Codepunkt eines Zeichens
text = "Hallo BEISPIEL"
for ch in text:
cp = ord(ch)
print(f'{ch}' → Codepunkt U+{cp:04X} (dezimal {cp}))
print()
# chr() ermittelt das Zeichen anhand des Codepunkts
for cp in [65, 97, 48, 0x4F60, 0x597D]:
ch = chr(cp)
print(f" Codepunkt U+{cp:04X} → Zeichen '{ch}'")
# =============================================
# Demo 2: als Bytefolge kodieren (encode)
# =============================================
print("\n" + "=" * 60)
print(Demo 2: Zeichenkette → UTF-8-Byte-Sequenz)
print("=" * 60)
texts = ["A", "Hello", Hello, "BEISPIEL Tutorial"]
for text in texts:
utf8_bytes = text.encode('utf-8')
hex_str = utf8_bytes.hex(' ').upper()
print(f"\nText: '{text}'")
print(f" Zeichenanzahl: {len(text)}")
print(fUTF-8-Byte-Anzahl: {len(utf8_bytes)})
print(f" UTF-8-Hexadezimal: {hex_str}")
# Byte-für-Byte-Analyse
print(f" Byte-Zerlegung:")
for i, b in enumerate(utf8_bytes):
bin_str = format(b, '08b')
# Byte-Typ bestimmen
if b < 0x80:
typ = "ASCII-Einzelbyte"
elif b >= 0xC0 and b < 0xE0:
typ = "Zwei-Byte-Führungsbyte"
elif b >= 0xE0 and b < 0xF0:
typ = "Drei-Byte-Führungsbyte"
elif b >= 0x80 and b < 0xC0:
typ = "Fortsetzungsbytes"
else:
typ = "Sonstiges"
print(fByte {i}: 0x{b:02X} = {bin_str} ({typ}))
# =============================================
# Demo 3: GBK-Kodierung (eine Nicht-Unicode-Kodierung, die in China häufig verwendet wird)
# =============================================
print("\n" + "=" * 60)
print(Demo 3: GBK-Kodierung (im Vergleich zu UTF-8))
print("=" * 60)
text = "Hallo BEISPIEL"
utf8_bytes = text.encode('utf-8')
gbk_bytes = text.encode('gbk')
print(fText: '{text}')
print(f" UTF-8 ({len(utf8_bytes)} byte): {utf8_bytes.hex(' ').upper()}")
print(f" GBK ({len(gbk_bytes)} byte): {gbk_bytes.hex(' ').upper()}")
# In GBK belegt jedes chinesische Zeichen 2 Bytes
print(f"\nHinweis: In GBK belegen chinesische Zeichen 2 Bytes; in UTF-8 belegen chinesische Zeichen 3 Bytes.")
print(f" Für chinesischen Text ist GBK kompakter als UTF-8")
print(fAber GBK unterstützt nicht alle Unicode-Zeichen (wie Emojis).)
# =============================================
# Demo 4: Dekodierungsfehler-Szenario
# =============================================
print("\n" + "=" * 60)
print("Demo 4: Dekodierungsfehler-Szenario (Ursache für verstümmelten Text)")
print("=" * 60)
# Chinesisch mit UTF-8 kodieren
original = Hello
utf8_data = original.encode('utf-8')
print(f"Originaltext: '{original}'")
print(fUTF-8-Kodierung: {utf8_data.hex(' ').upper()})
# Fehler 1: UTF-8-Daten mit GBK dekodieren
try:
decoded_wrong = utf8_data.decode('gbk')
print(fMit GBK dekodiert: '{decoded_wrong}' (unleserlich!))
except Exception as e:
print(fDekodierung mit GBK fehlgeschlagen: {e})
# Fehler 2: Mit Latin-1 dekodieren
decoded_latin = utf8_data.decode('latin-1')
print(fMit Latin-1 dekodiert: '{decoded_latin}' (sieht aus wie unleserliche westeuropäische Zeichen))
# =============================================
# Demo 5: Einen sinnvollen Decoder erstellen
# =============================================
print("\n" + "=" * 60)
print(Demo 5: Kodierungserkennung und sicheres Dekodieren)
print("=" * 60)
def safe_decode(data, encodings=['utf-8', 'gbk', 'latin-1']):
"""Versuche, mit mehreren Kodierungen zu dekodieren, und gib das erste erfolgreiche Ergebnis zurück"""
for enc in encodings:
try:
return data.decode(enc), enc
except (UnicodeDecodeError, LookupError):
continue
return data.decode('latin-1', errors='replace'), 'latin-1 (Fallback)'
# Test
test_cases = [
"Hallo BEISPIEL".encode('utf-8'),
"Hallo Welt".encode('utf-8'),
"Computer".encode('gbk'),
]
for data in test_cases:
result, encoding = safe_decode(data)
print(fDaten ({len(data)} Bytes) dekodiert mit {encoding}: '{result}')
print("\n" + "=" * 60)
print(Zusammenfassung: Strings sind Pythons interne Darstellung; Kodierung ist das Format für Speicherung/Übertragung.)
print(Führe die Kodierungskonvertierung immer an der I/O-Grenze durch und verwende intern immer den Typ str.)
print("=" * 60)
Aktueller Stand und Zukunft der Zeichenkodierung
Stand heute,UTF-8 hat sich als De-facto-Standard im Internet etabliert.。
Über 98% der Webseiten verwenden UTF-8-Kodierung.
Die JSON-Spezifikation erfordert UTF-8.
Fast alle modernen Programmiersprachen verwenden standardmäßig UTF-8 zur Textverarbeitung.
Unicode wird weiterhin erweitert.
Neue Versionen von Unicode fügen jedes Jahr neue Zeichen hinzu, darunter: seltene Schriften, historische Schriften und neue Emojis.
Bei der Kodierung hat sich die Frage von „ob es dargestellt werden kann“ zu „wie man effizient speichert und überträgt“ verschoben.
Weitere ErweiterungenWenn Sie heute ein neues Projekt starten, machen Sie mit der Wahl von UTF-8 nichts falsch. Es ist ein Kodierungsschema, das abwärtskompatibel zu ASCII, plattformübergreifend ist und alle Sprachen unterstützt. Das Einzige, worauf Sie achten müssen, ist – stellen Sie sicher, dass Ihr gesamter Stack (Frontend, Backend, Datenbank) UTF-8 verwendet, und mischen Sie nichts.