Intermédiaire 8 min de lecture 25 janvier 2025

Surrogate pair: comprendre, détecter et éviter les pièges

Tout semble correct, mais une longueur ne colle pas, une colonne déborde, une comparaison échoue. Les points de code au‑delà du BMP (emojis, symboles) utilisent des surrogate pairs en UTF‑16. Un seul caractère visible peut représenter deux unités. Voici ce que c’est, pourquoi cela casse vos traitements et comment s’en prémunir.

Qu'est-ce qu'un surrogate pair ?

En UTF‑16, un surrogate pair est un couple d’unités (haut + bas) représentant un point de code au‑delà de U+FFFF. Un seul symbole peut donc occuper deux unités.

Les notions essentielles à connaître :

1 BMP vs plans supplémentaires

Le BMP couvre U+0000 à U+FFFF. Au‑delà (U+10000 à U+10FFFF), UTF‑16 utilise des surrogate pairs.

BMP: U+0000–U+FFFF • Supplémentaires: U+10000–U+10FFFF

2 Paires de substitution UTF‑16

Une unité haute (U+D800–DBFF) suivie d’une unité basse (U+DC00–DFFF) représente un point de code unique.

High: D800–DBFF • Low: DC00–DFFF • 😀 (U+1F600) → D83D DE00

3 Exemples fréquents hors BMP

Les plus rencontrés dans les applications modernes :

Emojis (😀, 🚀, ❤️)
Symboles musicaux (𝄞), mathématiques (𝝠…)
Idéogrammes CJK extension B
Scripts historiques et pictogrammes

4 Marques techniques connexes

Éléments souvent combinés avec des surrogate pairs :

VS16 (U+FE0F) — sélecteur de style emoji
ZWJ (U+200D) — jonction de glyphes (familles, drapeaux)
Modificateurs de teint (U+1F3FB–U+1F3FF)
Clusters de graphèmes multi‑points de code

Problèmes classiques

Troncature au milieu d’un surrogate pair

Substr/LEFT() coupe une unité haute ou basse seule → caractère �, JSON invalide, insertion DB refusée.

Longueur incohérente selon les langages

JavaScript str.length compte les unités UTF‑16; Python compte les points de code → validations et limites faussées.

Regex et classes de caractères surprises

Selon le moteur, . ou \w n’englobe pas les points de code astrals, ou casse une paire en deux unités.

Sérialisation/encodage non strict

JSON/XML peuvent rejeter des paires non appariées; des conversions UTF‑16 ↔ UTF‑8 altèrent les données.

Exemple de problème courant :

# Substring coupée au milieu d’un surrogate pair (emoji)
string1 = "A😀B"
string2 = string1[0:3] # Coupe après l’unité haute D83D
assert is_valid_utf8(string2) # ❌ Échec / caractère �

Symptômes qui doivent vous alerter

🚨 Signaux d'alarme

!
Un diff montre un caractère modifié mais vous voyez le même emoji
!
Une limite à 10 caractères accepte 10 glyphes, mais la base refuse l’insertion
!
Une API renvoie “invalid surrogate pair” ou “unpaired surrogate”
!
Votre éditeur affiche un losange � ou des carrés à l’emplacement de certains symboles
!
Un copier‑coller d’emoji casse une commande ou une clé .env

Comment les détecter

Solution recommandée : Clean ASCII

Clean ASCII identifie les points de code au‑delà du BMP, les paires UTF‑16 non appariées et les séquences problématiques. L’analyse indique les positions, la valeur exacte et propose des corrections sûres.

✅ Détection automatique

Points de code > U+FFFF, paires hautes/basses, séquences invalides

📊 Analyse complète

Code points, offsets, aperçu binaire et UTF‑16

🧹 Nettoyage automatique

Réparation des paires non appariées, conversions sûres vers UTF‑8

💾 Export propre

Texte corrigé, conforme aux API et bases de données

Autres méthodes de détection

Affichage dans l'éditeur

Activez l’affichage des points de code et des glyphes invisibles (VS Code, JetBrains, Sublime)
Installez une extension qui surligne les caractères hors BMP et surrogates isolés

En ligne de commande (Unix)

# Repérer les séquences UTF‑8 4 octets (hors BMP)
grep -P "[\xF0-\xF4]" fichier.txt
# Valider l’UTF‑8 et signaler les erreurs
iconv -f UTF-8 -t UTF-8 -o /dev/null fichier.txt 2>&1
# Afficher les caractères problématiques
cat -A fichier.txt
# Inspecter les octets pour confirmer les paires
hexdump -C fichier.txt

En code

JavaScript

Array.from(str).map(c => c.codePointAt(0).toString(16))

Python

[f"{ord(c):04x}" for c in s if ord(c) > 0xFFFF]

Excel / Google Sheets

UNICODE(MID(cellule;position;1))

Nettoyer et prévenir

🚀 Solution rapide avec Clean ASCII

Avant d’écrire du code de parsing, utilisez Clean ASCII pour diagnostiquer et corriger immédiatement les surrogate pairs problématiques.

Détection automatique
Réparation des paires
Export immédiat

Méthodes techniques avancées

🔧 Normaliser

Utilisez des API conscientes des points de code (grapheme‑safe)
Convertissez et stockez en UTF‑8 strict (éviter les surrogates isolés)
Appliquez NFC/NFKC si nécessaire pour homogénéiser les séquences

🧹 Filtrer

Écrivez des fonctions sanitize_surrogates() pour refuser/réparer les paires non appariées
Remplacez proprement les séquences invalides par des équivalents sûrs
Bloquez les unités hautes/basses isolées à l’entrée

⚙️ Automatiser

Hooks pre-commit pour refuser surrogates isolés et fichiers non UTF‑8
Tests de validation sur la segmentation en graphèmes et les limites de champs
Linting Unicode sur le pipeline CI

Checklist rapide

Stockage en UTF-8 sans BOM
Découpe et longueur conscientes des graphèmes (UAX #29)
Éditeur affichant les points de code et surbrillance Unicode
Validation des surrogate pairs à l’entrée
Tests empêchant paires non appariées et encodages incorrects
Documentation sur BMP, plans supplémentaires et surrogate pairs

Conclusion

Les surrogate pairs ne sont pas visibles au premier coup d’œil, mais ils impactent les longueurs, les découpes et les encodages.

En détectant les points de code hors BMP, en validant les paires UTF‑16 et en adoptant des API “code point aware”, vous évitez une large part des bugs liés aux emojis et symboles modernes.

Analysez vos textes avec surrogate pairs

Utilisez notre outil pour repérer les points de code hors BMP, réparer les paires non appariées et fiabiliser vos traitements.

Analyser mon texte