Les pubs vous déplaisent ? Aller Sans pub Auj.

UTF-8 et Unicode Pourquoi un emoji a brisé votre base de données

Mis à jour le

Votre application a inséré un emoji et MySQL a lancé une erreur Incorrect string value. Voici pourquoi — les points de code versus les octets, le mensonge de MySQL utf8 vs utf8mb4, les paires de surrogats JavaScript, et la manière réelle de les corriger.

Votre application a inséré un emoji et MySQL a lancé une erreur de valeur de chaîne. Voici pourquoi — code points vs octets, le mensonge de MySQL utf8 vs utf8mb4, les paires de surrogats en JavaScript, et comment le corriger réellement.
ANNONCE · Supprimer ?

Votre application fonctionnait parfaitement. Puis un utilisateur a tapé un emoji dans un champ de texte, et MySQL a lancé Incorrect string value: '\xF0\x9F\x98\x80' for column 'bio'. Ou peut-être que l'emoji est apparu sans que rien ne se produise. Ou que l'INSERT a échoué entièrement et que vous avez perdu des données. Tout cela à cause d'un caractère de quatre octets que la colonne de votre base de données n'attendait pas.

Ce n'est pas un défaut de MySQL ni un bug de PHP. C'est une conséquence de la manière dont Unicode → UTF-8 fonctionne réellement, et une fois que vous l'avez compris, vous ne serez plus surpris par cela.

Code points vs octets : la différence réelle

Unicode attribue à chaque caractère un code point — un nombre. La lettre A est U+0041. Le signe euro est U+20AC. L'emoji 😀 est U+1F600. C'est l'identité abstraite du caractère.

UTF-8 est un encodage — une manière de stocker les code points sous forme d'octets. Le truc est que UTF-8 est de largeur variable : il utilise de 1 à 4 octets selon la valeur du code point. C'est ainsi qu'il reste compatible avec ASCII (tous les caractères ASCII sont 1 octet en UTF-8) tout en permettant d'encoder chaque caractère existant.

Les règles d'encodage :

  • U+0000 à U+007F (ASCII) → 1 octet
  • U+0080 à U+07FF (étendu latin, arabe, hébreu, etc.) → 2 octets
  • U+0800 à U+FFFF (la plupart des caractères CJK, ponctuation, symboles) → 3 octets
  • U+10000 à U+10FFFF (emojis, scripts rares, symboles mathématiques) → 4 octets

C'est pour cette raison que l'emoji 😀 (U+1F600) occupe 4 octets : son code point est au-dessus de U+FFFF.

Tailles en octets de UTF-8 : une table de référence

Voici ce que coûtent réellement les caractères courants en octets :

CaractèreDescriptionPoint de code UnicodeOctets UTF-8 (hexadécimal)Nombre d'octets
UNLettre latine majuscule AU+0041411
éLettre e avec accent aiguU+00E9C3 A92
Signe euroU+20ACE2 82 AC3
Caractère chinois « milieu »U+4E2DE4 B8 AD3
😀Emoji souriantU+1F600F0 9F 98 804
🔥Emoji feuU+1F525F0 9F 94 A54
𝕳H fraktur mathématiqueU+1D573F0 9D 95 B34

Pour le vérifier vous-même, utilisez le Calculateur de longueur de chaîne — il affiche à la fois le nombre de caractères et le nombre d'octets pour tout texte que vous collez. Collez 😀 et vous verrez 1 caractère mais 4 octets.

L'erreur de MySQL sur utf8

Voici où les développeurs sont victimes. MySQL dispose d'un jeu de caractères appelé utf8. Cela semble correct. C'est faux — le jeu de caractères utf8 de MySQL ne supporte que des séquences de 3 octets. Les emojis (4 octets) ne sont pas supportés. utf8 Le jeu de caractères UTF-8 complet dans MySQL est

(introduit dans MySQL 5.5.3, publié en 2010). Si votre colonne utilise utf8mb4 et qu'un utilisateur insère un emoji, MySQL soit tronque silencieusement les données soit lance : utf8 La solution :

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

Mettez également à jour la configuration de la connexion à la base de données de votre application. Dans MySQL PDO :

-- 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;

Le piège de VARCHAR(255)

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

dans MySQL signifie 255

VARCHAR(255) caractères , pas 255 octets — mais la limite de stockage d'une seule ligne est calculée en octets. Avec, chaque caractère peut prendre jusqu'à 4 octets, donc une utf8mb4colonne réserve jusqu'à 1 020 octets. Cela importe lorsque vous utilisez la limite par défaut de l'index prefix (767 octets) pour indexer les colonnes varchar : VARCHAR(255) Le problème des paires de surrogats en JavaScript

-- 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 utilise UTF-16 internement, pas UTF-8. Et UTF-16 a son propre encodage multi-unité pour les code points au-dessus de U+FFFF :

paires de surrogats — deux unités de code de 16 bits qui représentent ensemble un caractère. Cela signifie que

dans JavaScript compte les unités de code UTF-16, pas les caractères : String.length Pour les opérations de chaîne nécessitant une connaissance caractéristique, utilisez l'opérateur de diffusion ou

'😀'.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)

L'exemple de l'emoji familial vaut la peine d'être arrêté. 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)

est composé de quatre emojis reliés par des jointures de largeur nulle (U+200D). Une approche naïve 👨‍👩‍👧‍👦 vous donne 11. Les clusters graphémiques réels : 1. Cela importe si vous implémentez des limites de caractères — une limite basée sur .length se comportera de manière inattendue lorsque les utilisateurs saisissent des séquences d'emojis. String.length Comment vérifier l'encodage en pratique

dans PHP compte les octets, pas les caractères. Cela fait constamment tomber les développeurs PHP lorsqu'ils gèrent des chaînes multioctets — une chaîne de 10 emojis affichera une longueur de 40. Utilisez

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() lorsque vous souhaitez compter les caractères. mb_strlen() Vérification rapide

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

Si vous souhaitez voir le nombre de caractères et le nombre d'octets pour un texte arbitraire sans écrire de code, le

le gère instantanément — collez tout texte et il affiche le nombre de caractères, le nombre de mots et le nombre d'octets côte à côte. Calculateur de longueur de chaîne La checklist de la bug d'encodage

Jeu de caractères MySQL :

  • Est-il ? Vérifiez avec utf8mb4, et non pas utf8Connexion MySQL : SHOW CREATE TABLE.
  • Votre application envoie-t-elle ? Vérifiez votre DSN ou la configuration de connexion. SET NAMES utf8mb4PHP strlen vs mb_strlen :
  • Utilisez-vous des fonctions de comptage en octets où vous avez besoin de compter les caractères ? JavaScript .length :
  • Comptez-vous les unités de code où vous avez besoin de clusters graphémiques ? En-têtes HTTP :
  • Votre réponse envoie-t-elle Format de fichiers : Content-Type: text/html; charset=utf-8?
  • Vos fichiers sources et vos dumps SQL sont-ils enregistrés en UTF-8 sans BOM ? UTF-8 et Unicode : Pourquoi un emoji a brisé votre base de données 2
Envie d'une expérience sans pub ? Passez à la version sans pub

Installez nos extensions

Ajoutez des outils IO à votre navigateur préféré pour un accès instantané et une recherche plus rapide

Sur Extension Chrome Sur Extension de bord Sur Extension Firefox Sur Extension de l'opéra

Le Tableau de Bord Est Arrivé !

Tableau de Bord est une façon amusante de suivre vos jeux, toutes les données sont stockées dans votre navigateur. D'autres fonctionnalités arrivent bientôt !

ANNONCE · Supprimer ?
ANNONCE · Supprimer ?
ANNONCE · Supprimer ?

Coin des nouvelles avec points forts techniques

Impliquez-vous

Aidez-nous à continuer à fournir des outils gratuits et précieux

Offre-moi un café
ANNONCE · Supprimer ?