Keine Werbung mögen? Gehen Werbefrei Heute

UTF-8 und Unicode Warum dieses Emoji Ihre Datenbank zerstört

Aktualisiert am

Ihre App hat ein Emoji eingefügt und MySQL hat „Incorrect string value“ geworfen. Hier erfahren Sie, warum – Codepunkte vs. Bytes, das Lügen von MySQL utf8 vs. utf8mb4, JavaScript-Surrogate-Paare und wie man es tatsächlich behebt.

UTF-8 und Unicode: Warum das Emoji Ihr Database zerstört 1
ANZEIGE Entfernen?

Ihre Anwendung funktionierte bisher gut. Dann gab ein Benutzer ein Emoji in ein Textfeld ein, und MySQL warf Incorrect string value: '\xF0\x9F\x98\x80' for column 'bio'. Oder vielleicht verschwand das Emoji schweigend. Oder die gesamte INSERT-Anweisung scheiterte und Sie verloren Daten. Alles wegen eines vier-Byte-Zeichens, das Ihre Datenbankspalte nicht erwartete.

Das ist kein MySQL-Problem oder ein PHP-Fehler. Es ist eine Konsequenz davon, wie Unicode → UTF-8-Encodierung tatsächlich funktioniert, und sobald Sie das verstehen, werden Sie es nie mehr überraschen.

Code Points im Vergleich zu Bytes: der tatsächliche Unterschied

Unicode weist jedem Zeichen einen Code Point — eine Zahl — zu. Der Buchstabe A ist U+0041. Das Euro-Symbol ist U+20AC. Das 😀-Emoji ist U+1F600. Das ist die abstrakte Identität des Zeichens.

UTF-8 ist ein Codierung — eine Methode, um Code Points als Bytes zu speichern. Der Trick besteht darin, dass UTF-8 variable Breite hat: es verwendet 1 bis 4 Bytes, abhängig vom Code Point-Wert. So bleibt es mit ASCII kompatibel (alle ASCII-Zeichen sind 1 Byte in UTF-8) und kann gleichzeitig jedes existierende Zeichen codieren.

Die Codierungsregeln:

  • U+0000 bis U+007F (ASCII) → 1 Byte
  • U+0080 bis U+07FF (erweiterte Latein-Schreibweise, Arabisch, Hebräisch usw.) → 2 Bytes
  • U+0800 bis U+FFFF (die meisten CJK-Zeichen, Punktzeichen, Symbole) → 3 Bytes
  • U+10000 bis U+10FFFF (Emojis, seltene Schriften, mathematische Symbole) → 4 Bytes

Deswegen benötigt das 😀-Emoji (U+1F600) 4 Bytes: Sein Code Point liegt über U+FFFF.

Größe von UTF-8-Bytes: eine Referenztabelle

Hier ist, was gängige Zeichen tatsächlich in Bytes kosten:

ZeichenBeschreibungUnicode-CodepunktUTF-8-Bytes (Hex)Byte-Anzahl
ALateinische Großbuchstabe AU+0041411
éLateinische e mit AkzentU+00E9C3 A92
Euro-SymbolU+20ACE2 82 AC3
Chinesische Zeichen „Mittlere“U+4E2DE4 B8 AD3
😀Grinsendes Gesicht-EmojiU+1F600F0 9F 98 804
🔥Feuer-EmojiU+1F525F0 9F 94 A54
𝕳Mathematisches Fraktur-HU+1D573F0 9D 95 B34

Um dies selbst zu überprüfen, verwenden Sie die Zeichenlängenrechner — es zeigt sowohl die Zeichenanzahl als auch die Bytes-Anzahl für jeden Text, den Sie einfügen. Fügen Sie 😀 ein und Sie sehen 1 Zeichen, aber 4 Bytes.

Das MySQL-utf8-Mythos

Hier wird Entwicklerinnen und Entwickler verbrannt. MySQL besitzt eine Zeichensatz-Bezeichnung namens utf8. Klingt logisch. Es ist falsch — MySQLs utf8 unterstützt nur bis zu 3-Byte-Sequenzen. Emojis (4 Bytes) werden nicht unterstützt.

Der tatsächliche vollständige UTF-8-Zeichensatz in MySQL ist utf8mb4 (eingeführt in MySQL 5.5.3, veröffentlicht 2010). Wenn Ihre Spalte utf8 verwendet und jemand ein Emoji einfügt, wird MySQL entweder die Daten schweigend abgeschnitten oder wirft:

Incorrect string value: '\xF0\x9F\x98\x80' for column 'bio' at row 1

Die Lösung:

-- Convert the table
ALTER TABLE users CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

-- Or for a specific column
ALTER TABLE users MODIFY bio TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

-- And set your connection charset
SET NAMES utf8mb4;

Auch aktualisieren Sie Ihre Anwendungskonfiguration für die Datenbankverbindung. Bei MySQL PDO:

$pdo = new PDO($dsn, $user, $pass, [
    PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8mb4"
]);

Der VARCHAR(255)-Falle

VARCHAR(255) in MySQL bedeutet 255 Zeichen, nicht 255 Bytes — aber die Speicherbegrenzung für eine einzelne Zeile wird in Bytes berechnet. Mit utf8mb4kann jedes Zeichen bis zu 4 Bytes benötigen, wodurch eine VARCHAR(255) Spalte bis zu 1.020 Bytes reserviert. Dies spielt eine Rolle, wenn Sie InnoDBs Standardgrenze für Präfix-Indizes (767 Bytes) für varchar-Spalten verwenden:

-- This fails on older MySQL with default innodb_large_prefix=OFF
CREATE INDEX idx_email ON users (email);  -- email is VARCHAR(255) utf8mb4

-- Fix: use a prefix index
CREATE INDEX idx_email ON users (email(191));  -- 191 * 4 = 764 bytes, under 767

-- Or enable large prefixes (MySQL 5.7+, on by default in 8.0)
-- Set innodb_large_prefix = ON in my.cnf

JavaScript und das Problem der Surrogate-Paare

JavaScript verwendet intern UTF-16, nicht UTF-8. Und UTF-16 hat seine eigene mehrfache Codierung für Code Points über U+FFFF: Surrogate-Paare — zwei 16-Bit-Codes, die gemeinsam ein Zeichen darstellen.

Das bedeutet, String.length in JavaScript zählt die Anzahl der UTF-16-Codes, nicht Zeichen:

'😀'.length        // → 2 (two UTF-16 surrogate code units)
[...'😀'].length   // → 1 (spread operator uses Unicode code points)

// Checking the character at index 0
'😀'[0]            // → '\uD83D' (the high surrogate, not the emoji)
'😀'.codePointAt(0) // → 128512 (0x1F600, correct)

Für String-Operationen, die zeichenbewusst sein müssen, verwenden Sie den Spread-Operator oder Intl.Segmenter:

// Count actual grapheme clusters
const segmenter = new Intl.Segmenter();
const chars = [...segmenter.segment('👨‍👩‍👧‍👦')];
chars.length     // → 1 (family emoji is one grapheme cluster)
'👨‍👩‍👧‍👦'.length  // → 11 (UTF-16 code units)

Das Beispiel mit dem Familien-Emoji ist wertvoll, um innezuhalten. 👨‍👩‍👧‍👦 ist vier Emojis, verbunden durch Zero Width Joiners (U+200D). Ein naives .length gibt Ihnen 11. Die tatsächlichen Grapheme-Cluster: 1. Dies spielt eine Rolle, wenn Sie Zeichenbegrenzungen implementieren — eine Grenze basierend auf String.length verhält sich unerwartet, wenn Benutzer Emoji-Folgen eingeben.

Wie man die Codierung praktisch überprüft

Python

s = '😀'
print(len(s))                    # 1 (Python 3 counts code points)
print(len(s.encode('utf-8')))    # 4 bytes
print(s.encode('utf-8').hex())   # f09f9880

PHP

$s = '😀';
echo strlen($s);          // 4 (bytes, not characters)
echo mb_strlen($s);       // 1 (characters)
echo mb_strlen($s, '8bit'); // 4 (bytes, explicit)

strlen() in PHP zählt Bytes, nicht Zeichen. Dies fängt PHP-Entwickler ständig ein, wenn sie mit mehrbyte-Zeichen arbeiten — eine 10-Emoji-Zeichenkette wird eine Länge von 40 anzeigen. Verwenden Sie mb_strlen() bei Bedarf der Zeichenanzahl.

MySQL

-- Check charset of a table
SHOW CREATE TABLE users\G

-- Check charset of a specific column
SELECT CHARACTER_SET_NAME, COLLATION_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'users' AND COLUMN_NAME = 'bio';

-- Character count vs byte count
SELECT CHAR_LENGTH('😀'), LENGTH('😀');
-- → 1, 4

Schnelle Überprüfung

Wenn Sie die Bytes- und Zeichenanzahl für beliebige Texte ohne Code sehen möchten, kann die Zeichenlängenrechner das sofort lösen — fügen Sie beliebigen Text ein und es zeigt Zeichenanzahl, Wortanzahl und Bytesanzahl nebeneinander an.

Die Codierungsfehler-Checkliste

  • MySQL-Zeichensatz: Ist es utf8mb4sein, nicht utf8? Prüfen Sie mit SHOW CREATE TABLE.
  • MySQL-Verbindung: Ist Ihre Anwendung SET NAMES utf8mb4? Prüfen Sie Ihre DSN oder Verbindungskonfiguration.
  • PHP strlen vs mb_strlen: Verwenden Sie Sie Byte-Zählfunktionen, wo Sie Zeichenanzahlen benötigen?
  • JavaScript .length: Zählen Sie Sie Code-Einheiten, wo Sie Grapheme-Cluster benötigen?
  • HTTP-Header: Sendet Ihre Antwort Content-Type: text/html; charset=utf-8?
  • Datei-Codierung: Sind Ihre Quelldateien und SQL-Exporte als UTF-8 ohne BOM gespeichert?
Möchten Sie werbefrei genießen? Werde noch heute werbefrei

Erweiterungen installieren

IO-Tools zu Ihrem Lieblingsbrowser hinzufügen für sofortigen Zugriff und schnellere Suche

Zu Chrome-Erweiterung Zu Kantenerweiterung Zu Firefox-Erweiterung Zu Opera-Erweiterung

Die Anzeigetafel ist eingetroffen!

Anzeigetafel ist eine unterhaltsame Möglichkeit, Ihre Spiele zu verfolgen. Alle Daten werden in Ihrem Browser gespeichert. Weitere Funktionen folgen in Kürze!

ANZEIGE Entfernen?
ANZEIGE Entfernen?
ANZEIGE Entfernen?

Nachrichtenecke mit technischen Highlights

Beteiligen Sie sich

Helfen Sie uns, weiterhin wertvolle kostenlose Tools bereitzustellen

Kauf mir einen Kaffee
ANZEIGE Entfernen?