Unicode- und Emoji-Kodierung: Codepunkte bis Rendering
Unicode: Der Kodierungsstandard, der alle Zeichen vereint
Unicode weist jedem Zeichen fast aller Schriftsysteme der Welt eine eindeutige Nummer (Codepunkt) zu.
| Konzept | Beschreibung | Beispiel |
|---|---|---|
| Codepunkt (Code Point) | Eindeutige Nummer des Zeichens | U+4E2D (中) |
| Codeebene | Jeweils 65536 Codepunkte bilden eine Ebene | BMP (Ebene 0) |
| BMP | Basic Multilingual Plane, U+0000~U+FFFF | Die meisten häufigen Zeichen |
| Ergänzungsebene | U+10000~U+10FFFF | Emoji, antike Schriften, seltene Zeichen |
1. Die UTF-Kodierungsfamilie
| Kodierung | Bytelänge | Kodierung von "中" | Merkmale |
|---|---|---|---|
| UTF-8 | 1-4 Bytes | E4 B8 AD | Variabel, ASCII-kompatibel, Web-Standard |
| UTF-16 | 2 oder 4 Bytes | 4E2D | Variabel, interne JS-Nutzung |
| UTF-32 | Fest 4 Bytes | 00004E2D | Fest, hoher Speicherverbrauch |
UTF-8-Kodierungsregeln
| Codepunktbereich | Bytes | Kodierungsvorlage |
|---|---|---|
| U+0000 ~ U+007F | 1 | 0xxxxxxx |
| U+0080 ~ U+07FF | 2 | 110xxxxx 10xxxxxx |
| U+0800 ~ U+FFFF | 3 | 1110xxxx 10xxxxxx 10xxxxxx |
| U+10000 ~ U+10FFFF | 4 | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx |
Kodierungsbeispiel
Zeichen: 中
Codepunkt: U+4E2D = 0100 1110 0010 1101
UTF-8 (3 Bytes):
1110xxxx 10xxxxxx 10xxxxxx
1110 0100 10 111000 10 101101
= E4 B8 AD
UTF-16 (2 Bytes):
4E2D
UTF-32 (4 Bytes):
00004E2D
2. Unicode-Fallen in JavaScript
Falsche Zeichenfolgenlänge
'中'.length // 1 ✅
'𠮷'.length // 2 ❌ (Zeichen der Ergänzungsebene, Ersatzpaar)
'👨👩👧👦'.length // 11 ❌ (Emoji-ZWJ-Sequenz)
'🏳️🌈'.length // 6 ❌ (Flaggen-Emoji)
Ursache: JS verwendet UTF-16
Zeichen der Ergänzungsebene (Codepunkt > U+FFFF) erfordern Ersatzpaare (2 UTF-16-Codeeinheiten):
Zeichen: 𠮷 (U+20BB7)
UTF-16-Ersatzpaar: D842 DF87
JS: length = 2 (zwei Codeeinheiten)
Korrekte Verarbeitung
// Methode 1: Spread-Operator
[...'𠮷'].length // 1 ✅
[...'👨👩👧👦'].length // 1 ✅ (aber ZWJ-Sequenz als eins gezählt)
// Methode 2: Array.from
Array.from('𠮷').length // 1 ✅
// Methode 3: for...of
for (const char of '𠮷👨👩👧👦') {
console.log(char); // zeichenweise Iteration
}
Codepunktoperationen
// Codepunkt abrufen
'中'.codePointAt(0) // 20013 (0x4E2D)
'𠮷'.codePointAt(0) // 134047 (0x20BB7)
// Zeichen aus Codepunkt erstellen
String.fromCodePoint(0x4E2D) // '中'
String.fromCodePoint(0x20BB7) // '𠮷'
// Hinweis: charCodeAt kann die Ergänzungsebene nicht verarbeiten
'𠮷'.charCodeAt(0) // 55362 (nur das erste Ersatzzeichen)
3. Tiefenanalyse der Emoji-Kodierung
Vier Emoji-Typen
| Typ | Beispiel | Codepunkt | Länge |
|---|---|---|---|
| Basis-Emoji | 😀 | U+1F600 | 1 (Codepunkt) |
| Emoji mit Modifikator | 👩🏽 | U+1F469 + U+1F3FD | 2 (Codepunkte) |
| ZWJ-Sequenz | 👨👩👧👦 | Mehrere Codepunkte + ZWJ | 7 (Codepunkte) |
| Flaggen-Emoji | 🇨🇳 | U+1F1E8 + U+1F1F3 | 2 (Codepunkte) |
Zero-Width-Joiner (ZWJ)
ZWJ (U+200D) verbindet mehrere Emoji zu einem zusammengesetzten Emoji:
👨 (U+1F468) + ZWJ (U+200D) + 👩 (U+1F469) + ZWJ (U+200D) + 👧 (U+1F467) + ZWJ (U+200D) + 👦 (U+1F466)
= 👨👩👧👦 (Vier-Personen-Familie)
Hautfarbenmodifikatoren
Hautfarbenmodifikatoren (U+1F3FB ~ U+1F3FF) ändern die Hautfarbe des Emoji:
👩 (U+1F469) + 🏽 (U+1F3FD) = 👩🏽 (Frau mit mittlerer Hautfarbe)
| Modifikator | Codepunkt | Hautfarbe |
|---|---|---|
| U+1F3FB | 🏻 | Helle Hautfarbe |
| U+1F3FC | 🏼 | Mittelhelle Hautfarbe |
| U+1F3FD | 🏽 | Mittlere Hautfarbe |
| U+1F3FE | 🏾 | Mitteldunkle Hautfarbe |
| U+1F3FF | 🏿 | Dunkle Hautfarbe |
Flaggen-Emoji
Flaggen bestehen aus zwei regionalen Indikatorsymbolen (ISO 3166-1 alpha-2):
🇨 (U+1F1E8) + 🇳 (U+1F1F3) = 🇨🇳 (China)
🇺 (U+1F1FA) + 🇸 (U+1F1F8) = 🇺🇸 (USA)
🇯 (U+1F1EF) + 🇵 (U+1F1F5) = 🇯🇵 (Japan)
4. Variantenselektoren
VS16 (U+FE0F) wandelt die Textdarstellung in Emoji-Darstellung um:
❤ (U+2764) → Textdarstellung: ❤ (schwarzes Herz)
❤️ (U+2764 + U+FE0F) → Emoji-Darstellung: ❤️ (rotes Herz)
| Zeichen | Ohne VS16 | Mit VS16 |
|---|---|---|
| ☕ | ☕ (Text) | ☕️ (Emoji) |
| ⚡ | ⚡ (Text) | ⚡️ (Emoji) |
| ❤ | ❤ (Text) | ❤️ (Emoji) |
5. Verwendung von Tool-Bibliotheken zur Unicode-Verarbeitung
Unicode-Kodierung/Dekodierung
- Öffnen Sie das Unicode-Kodierungs-/Dekodierungstool
- Geben Sie Text oder Codepunkt ein
- Prüfen Sie die Konvertierungsergebnisse:
- Text → Unicode-Escapesequenz (
\u4E2D) - Codepunkt → Zeichen
- UTF-8/16/32-Bytesequenz
- Text → Unicode-Escapesequenz (
Emoji-Kodierung
- Öffnen Sie das Emoji-Kodierungstool
- Geben Sie ein Emoji ein
- Prüfen Sie die vollständigen Kodierungsinformationen:
- Codepunktsequenz
- UTF-8-Bytes
- ZWJ/Modifikator-Zerlegung
- CLDR-Kurzname
6. Häufige Fallstricke und Lösungen
Fallstrick 1: Zeichenfolgenabschneidung
// Falsch: Abschneiden kann Ersatzpaare trennen
text.substring(0, 10) // kann ungültige Zeichen erzeugen
// Richtig: nach Codepunkt abschneiden
function sliceByCodePoint(str, start, end) {
const chars = [...str];
return chars.slice(start, end).join('');
}
Fallstrick 2: Zeichenfolgenumkehr
// Falsch: Umkehr zerstört kombinierte Zeichen
'👨👩👧👦'.split('').reverse().join('') // unleserlich
// Richtig: nach Codepunkt umkehren
[...'👨👩👧👦'].reverse().join('') // ✅
Fallstrick 3: Regex-Übereinstimmung
// Falsch: . stimmt nicht mit Zeichen der Ergänzungsebene überein
/^.$/.test('𠮷') // false
// Richtig: u-Flag verwenden
/^.$/u.test('𠮷') // true
Fallstrick 4: Sortierung
// Standardsortierung nach UTF-16-Codeeinheiten
['中', '𠮷', 'a'].sort() // möglicherweise nicht wie erwartet
// Richtig: nach Codepunkt sortieren
['中', '𠮷', 'a'].sort((a, b) =>
a.codePointAt(0) - b.codePointAt(0)
)
7. Praktische APIs
// Normalisierung (Umgang mit kombinierter Zeichenäquivalenz)
'café'.normalize('NFC') // café (kombiniert)
'café'.normalize('NFD') // café (zerlegt)
// Emoji erkennen
function isEmoji(char) {
const code = char.codePointAt(0);
return (code >= 0x1F600 && code <= 0x1F64F) || // Emoticons
(code >= 0x1F300 && code <= 0x1F5FF) || // Verschiedene Symbole
(code >= 0x1F680 && code <= 0x1F6FF) || // Transport
(code >= 0x1F900 && code <= 0x1F9FF); // Ergänzend
}
// Tatsächliche Zeichenlänge abrufen
function graphemeLength(str) {
const segmenter = new Intl.Segmenter('de', { granularity: 'grapheme' });
return [...segmenter.segment(str)].length;
}
graphemeLength('👨👩👧👦') // 1 ✅
graphemeLength('🏳️🌈') // 1 ✅
#Unicode#Emoji#UTF-8#字符编码#码点