Base64 et UTF-8 : diagnostiquer les textes chinois, les emoji et les caractères de remplacement

Tracez le texte via les octets Unicode et UTF-8, distinguez les erreurs de décodage de la normalisation et préservez les entrées binaires non valides.

Lorsque Base64 semble « casser le chinois », le problème est généralement la frontière entre les caractères et les octets. Base64 ne sait pas qu'une séquence représente du texte chinois ou des emoji. Il préserve les octets ; l'application choisit la manière dont ces octets sont produits et interprétés.

Suivez les octets

Le caractère 中 est U+4E2D. Dans UTF-8 il utilise les octets E4 B8 AD, dont la représentation Base64 est 5Lit. Un codage de caractères différent produit des octets différents et donc une valeur Base64 différente. Mettez-vous d'accord sur UTF-8 aux deux extrémités avant de comparer les chaînes codées.

const bytes = new TextEncoder().encode('中');
console.log([...bytes]); // [228, 184, 173]
console.log([...bytes].map(b => b.toString(16)).join(' ')); // e4 b8 ad

Emoji illustre une autre distinction : JavaScript '😊'.length est deux unités de code UTF-16, tandis que sa représentation UTF-8 comporte quatre octets. Aucun des deux nombres ne constitue à lui seul un décompte des caractères visibles perçus par l'utilisateur dans un texte arbitraire. Utilisez la longueur en octets pour les calculs de fichiers et de transport.

Rendre le remplacement visible

Le caractère de remplacement � peut signifier qu'un décodeur antérieur a déjà perdu des informations. Une fois que des octets inconnus ont été remplacés dans une chaîne et que cette chaîne est réencodée, les valeurs d'origine ne peuvent généralement pas être reconstruites. Inspectez le tableau d’octets brut décodé avant d’effectuer d’autres conversions.

const decoder = new TextDecoder('utf-8', { fatal: true });
console.log(decoder.decode(Uint8Array.of(0xe4, 0xb8, 0xad))); // 中
try { decoder.decode(Uint8Array.of(0xff)); }
catch { console.log('Invalid UTF-8; preserve the original byte.'); }

La normalisation est une opération distincte

Deux chaînes peuvent sembler identiques tout en contenant des séquences Unicode différentes. La lettre accentuée é peut être un caractère précomposé ou un e suivi d’un accent combinant. UTF-8 et Base64 préservent correctement cette différence. Si votre application exige un texte normalisé, choisissez explicitement une forme de normalisation et appliquez-la avant l’encodage ; ne présentez jamais un résultat modifié comme une réparation qui préserve les octets.

Vérifiez également les caractères invisibles, les fins de ligne et une éventuelle marque d'ordre des octets lorsque les résultats diffèrent. Ce sont de vrais octets d’entrée, pas nécessairement des bugs d’encodeur. Pour les messages signés et les sommes de contrôle, même une différence invisible modifie le résultat.

Un ordre de dépannage fiable est le suivant : préserver la source, inspecter les octets, identifier le codage de caractères attendu, décoder avec une gestion explicite des erreurs, et ensuite seulement envisager la normalisation. MDN : btoa() explique pourquoi les appels btoa directs du navigateur ne conviennent pas au texte Unicode arbitraire. Les fichiers binaires doivent contourner entièrement le décodage du texte.