Inspecteur de caractères Unicode
Décompose un texte en points de code avec noms, catégories, blocs et encodages, et signale les caractères invisibles, bidi et sosies.
Ce que fait cet outil
Collez n’importe quel texte et il revient décomposé en les caractères individuels dont il est réellement fait. Chacun reçoit son nom Unicode, son numéro U+, quel genre de caractère il est, à quel système d’écriture et à quel bloc il appartient, les octets qu’il occupe en UTF-8 et en UTF-16, et la séquence d’échappement qu’il exige dans six formats différents.
La plupart des gens arrivent ici parce que quelque chose ne colle pas. Une chaîne est plus longue qu’elle n’en a l’air, deux valeurs identiques à l’écran refusent d’être égales, un nom d’utilisateur a été rejeté à cause de caractères qui ressemblent à des lettres ordinaires, ou une ligne de code se lit autrement qu’elle ne se comporte. Les quatre ont la même forme : un caractère qui fait quelque chose que l’écran ne montre pas.
Caractères, points de code et graphèmes
Le mot « caractère » désigne au moins trois choses différentes, et les confondre est le point de départ de presque toute la confusion autour d’Unicode. Un point de code est une valeur du standard Unicode, écrite U+0041 ou U+1F600. Un graphème est ce qu’un lecteur compte comme un caractère. Ce ne sont fréquemment pas les mêmes nombres.
- Un « a » ordinaire vaut 1 graphème, 1 point de code et 1 unité UTF-16 : tout le monde est d’accord.
- U+00E9, le é précomposé, vaut 1 graphème, 1 point de code et 1 unité UTF-16.
- U+0065 U+0301, la même lettre écrite comme e plus un accent combinant, vaut 1 graphème mais 2 points de code et 2 unités UTF-16.
- U+1F600, le visage souriant, vaut 1 graphème et 1 point de code mais 2 unités UTF-16 : il vit au-dessus du BMP, donc JavaScript le stocke en paire de substitution.
- L’émoji de famille vaut 1 graphème, 5 points de code et 8 unités UTF-16 : trois personnes reliées par deux liaisons invisibles.
L’émoji de famille est le cas qui prend les gens de court. Il ressemble à une seule chose, JavaScript annonce une longueur de 8, et une colonne de base de données de 5 caractères ne le contiendra pas. Cet outil regroupe les points de code sous le graphème qu’ils construisent, de sorte que les deux lectures sont visibles en même temps au lieu de devoir être réconciliées de tête.
Les caractères que vous ne pouvez pas voir
Un nombre surprenant de caractères Unicode ne dessine rien du tout. Ils existent pour contrôler le comportement du texte qui les entoure, et comme ils ne laissent aucune trace ils survivent au copier, au coller et à la relecture sans que personne ne le remarque. Voici ceux que l’outil désigne au lieu de se contenter de les énumérer :
- Espace, liaison et anti-liaison sans chasse (U+200B, U+200D, U+200C) : aucune largeur, aucune marque, et ils cassent toute comparaison de chaînes qu’ils touchent.
- Trait d’union conditionnel (U+00AD) : une suggestion de coupure de mot, invisible jusqu’à ce qu’elle serve.
- Espace insécable (U+00A0) : identique à l’œil à une espace et pourtant un autre caractère, ce qui explique sa longévité dans les fichiers de configuration.
- Marque d’ordre des octets (U+FEFF) : souvent laissée en tête de fichier, où elle devient un premier caractère invisible de la première valeur.
- Contrôles et forçages bidi (de U+202A à U+202E, de U+2066 à U+2069) : ils réordonnent le texte autour d’eux.
Les contrôles bidi méritent une mention à part. Ils ont été ajoutés pour que l’hébreu et l’arabe puissent se mêler correctement au texte latin, et ils remplissent bien cet office. Mais un forçage placé dans un commentaire ou dans une chaîne peut faire afficher une ligne de code dans un ordre totalement différent de celui que lit le compilateur — ce qui veut dire qu’un relecteur peut approuver du code qui fait tout autre chose. Cette famille de ruses porte le nom de Trojan Source, et c’est pourquoi cet outil signale chaque contrôle bidi séparément des autres caractères invisibles.
Caractères sosies et mélange d’écritures
Unicode contient beaucoup de caractères visuellement identiques à des lettres ASCII sans en être. Le а cyrillique, le ο grec et le a latin occupent des points de code différents et se dessinent pareil dans la plupart des polices. Un domaine ou un identifiant bâti avec eux paraît correct et pointe ailleurs.
Signaler chaque sosie non-ASCII serait inutile : cela peindrait en rouge chaque mot russe, grec ou hébreu, et un avertissement qui se déclenche sur du texte ordinaire est un avertissement que les gens apprennent à ignorer. La règle retenue ici est donc celle du standard de sécurité d’Unicode : un mot est suspect quand les lettres qu’il contient viennent de plus d’un système d’écriture :
- « paypal.com » écrit entièrement en lettres latines : rien à en dire.
- Le même mot avec un er et un a cyrilliques devant le « ypal » latin : un mot, deux systèmes d’écriture, signalé.
- Un mot russe entier en cyrillique : un seul système d’écriture, texte ordinaire, non signalé.
- Une phrase japonaise mêlant han, hiragana et katakana : trois écritures, un seul système d’écriture, non signalée.
Ce dernier cas est la raison pour laquelle la règle demande du soin. Le japonais s’écrit avec trois écritures à la fois et le coréen en mêle deux, si bien qu’un test naïf du type « plus d’une écriture est suspect » traiterait chaque phrase japonaise comme une attaque. Les combinaisons qui forment véritablement un seul système d’écriture sont traitées comme telles, ce qui garde à l’avertissement son sens.
Normalisation, ou pourquoi deux chaînes identiques diffèrent
Certains caractères peuvent s’écrire de plusieurs façons. La lettre é est soit l’unique point de code U+00E9, soit la paire U+0065 U+0301 : un e ordinaire suivi d’un accent aigu combinant. Les deux s’affichent à l’identique. Aucune n’est fausse. Elles ne sont pas égales.
La normalisation consiste à choisir une graphie, et elle a quatre formes. NFC compose partout où elle le peut et c’est ce que le web suppose par défaut ; NFD décompose au contraire. Les deux formes K vont plus loin et replient aussi les caractères de compatibilité — le A pleine chasse devient un A ordinaire, et la ligature fi devient fi — ce qui est utile pour la recherche et la comparaison, et destructeur pour le stockage.
Le panneau présenté ici montre les quatre formes de ce que vous collez et marque celles qui diffèrent de l’entrée. Si aucune ne diffère, le texte est déjà normalisé et l’écart que vous cherchez est ailleurs. Si certaines diffèrent, vous l’avez trouvé.
Noms, catégories, écritures et blocs
Chaque caractère attribué porte quatre éléments d’identité, et ils répondent à des questions différentes. Le nom, c’est le caractère lui-même. La catégorie générale dit quel genre de chose il est : une lettre minuscule, une marque combinante, un symbole monétaire, un contrôle de formatage. L’écriture est le système d’écriture auquel il appartient. Le bloc est la plage de points de code où il a été attribué, ce qui est un fait d’organisation et non de linguistique.
Bloc et écriture se confondent facilement et divergent souvent. Les lettres latines vivent dans au moins une demi-douzaine de blocs, et un bloc peut contenir des caractères de plusieurs écritures. Quand vous voulez savoir « quel alphabet est-ce », la réponse est l’écriture ; quand vous voulez savoir « où cela se range-t-il dans le standard », c’est le bloc.
Les noms, les écritures et les blocs sont affichés ici en anglais dans toutes les langues, délibérément. Ce sont des identifiants définis par le standard, pas de la prose : U+200D s’appelle ZERO WIDTH JOINER partout dans le monde, et le traduire le rendrait impossible à rechercher.
Encodages et échappements
Un point de code est un nombre ; il ne devient des octets qu’une fois un encodage choisi. UTF-8 emploie de un à quatre octets et laisse l’ASCII inchangé, ce qui explique sa victoire. UTF-16 emploie une ou deux unités de 16 bits, et c’est de cela que sont faites les chaînes JavaScript — d’où le .length qui annonce 2 pour un seul émoji.
U+0041 41 # UTF-8 : un octet, comme en ASCII U+00E9 C3 A9 # UTF-8 : deux octets U+20AC E2 82 AC # UTF-8 : trois octets U+1F600 F0 9F 98 80 # UTF-8 : quatre octets
Chaque ligne donne aussi le caractère écrit en échappement pour JavaScript, JSON, HTML, CSS, les URL et Python, prêt à copier. Ils ne sont pas tous identiques, et la différence compte au-dessus du BMP : JavaScript et Python disposent d’un échappement par point de code et s’en servent, tandis que JSON n’en a aucun et doit épeler un émoji par ses deux moitiés de substitution.
Questions fréquentes
- Pourquoi ma chaîne est-elle plus longue que le nombre de caractères que je vois ?
- Soit elle contient des caractères invisibles, soit elle contient des graphèmes bâtis sur plusieurs points de code. Les deux sont montrés ici : les caractères invisibles sont signalés, et les graphèmes à plusieurs points de code sont regroupés pour que vous voyiez le caractère unique et les plusieurs valeurs qui le composent.
- Deux chaînes paraissent identiques sans être égales. Pourquoi ?
- Le plus souvent la normalisation. Une lettre accentuée peut être un point de code ou une lettre plus une marque combinante, et les deux se dessinent pareil. Collez chaque chaîne ici et comparez les listes de points de code, ou regardez quelles formes de normalisation diffèrent de l’entrée.
- Qu’est-ce qu’une espace sans chasse et comment est-elle arrivée dans mon texte ?
- C’est un caractère sans largeur et sans marque, servant à suggérer une possibilité de retour à la ligne. Il arrive généralement en copiant du texte depuis une page web, un traitement de texte ou une messagerie, et comme rien ne s’affiche, il survit à toutes les relectures jusqu’à ce qu’une comparaison de chaînes échoue.
- Quelle est la différence entre un point de code et un caractère ?
- Un point de code est une valeur Unicode. Un caractère au sens courant est un graphème — ce qu’un lecteur compte pour un — et un graphème peut valoir plusieurs points de code. Un émoji de famille, c’est cinq points de code et un graphème.
- Pourquoi l’outil dit-il que certains caractères n’ont pas de nom ?
- Pour quatre raisons différentes, et il précise laquelle. Les substituts et les points de code à usage privé n’ont pas de nom dans le standard, les non attribués n’ont rien à nommer, et un caractère d’un bloc historique rarement consulté a un nom que cette version n’embarque pas : la table des noms garde les blocs d’où les gens collent réellement plutôt que de les livrer tous.
- Un avertissement de mélange d’écritures prouve-t-il une attaque ?
- Non. Il signifie qu’un mot unique tire ses lettres de plus d’un système d’écriture, ce qui est la forme d’un nom usurpé et aussi une chose que le texte ordinaire fait à l’occasion. C’est une raison de regarder, pas un verdict.
- Ce que je colle est-il envoyé à un serveur ?
- Non. La base de caractères est téléchargée avec la page et tout s’exécute dans votre navigateur ; rien de ce que vous tapez n’est téléversé ni journalisé, et cela fonctionne sans connexion réseau.