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.

Entrée

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.

Caractères cachés, et le texte sorti d’un modèle d’IA

Une bonne part du texte collé ici n’a pas été tapée par la personne qui le colle : il vient d’un document, d’une page web, d’un ticket ou de la réponse d’un assistant, et la question qui l’accompagne est de savoir s’il transporte des caractères invisibles à l’œil. C’est la question à laquelle cet outil répond, et il vaut la peine de dire jusqu’où va la réponse : le texte est décomposé en points de code, chacun avec son nom lorsque la norme lui en donne un, et ceux qui méritent un second regard sont comptés sur une ligne au-dessus de la liste. Si cette ligne n’apparaît pas, rien n’a été trouvé.

Ce qu’un caractère caché prouve est plus étroit que ne le laisse croire presque tout ce qui s’écrit sur le sujet. Rien dans un point de code n’enregistre sa provenance : celui arrivé en copiant depuis un gestionnaire de contenu et celui arrivé avec une réponse générée sont la même valeur. Des caractères invisibles ont bien été signalés dans la sortie de grands modèles de langue, et dans le cas qui a retenu le plus d’attention — une espace insécable étroite là où une espace ordinaire avait sa place — le constructeur du modèle a répondu qu’il s’agissait d’un sous-produit de l’entraînement et non de quelque chose de placé là exprès, et les signalements ont cessé en quelques jours. Une trouvaille ici dit donc quoi supprimer. Elle ne dit pas ce qui a écrit la phrase.

  • Des espaces qui ne sont pas l’espace ordinaire. L’espace insécable (U+00A0) et l’espace insécable étroite (U+202F) sont les deux les plus souvent nommées ; chacune fait à peu près la largeur d’une espace, chacune est un autre caractère, et cet outil signale les deux.
  • Des caractères sans aucune largeur. L’espace sans chasse (U+200B), le jointeur de mots (U+2060) et la marque d’ordre des octets (U+FEFF) ne dessinent rien, survivent à la relecture et cassent la première comparaison qu’ils touchent. Ils sont signalés comme invisibles.
  • Le bloc Tags (de U+E0000 à U+E007F). Il contient un point de code invisible pour chaque caractère ASCII imprimable — U+E0041 se nomme TAG LATIN CAPITAL LETTER A — si bien qu’une phrase entière peut s’épeler dans un texte qui n’affiche absolument rien. Chacun est nommé ici et signalé comme invisible.
  • La ponctuation qu’on accuse et qui n’est pas cachée. Le tiret cadratin (U+2014) et l’apostrophe typographique (U+2019) dessinent une marque visible, si bien que rien ici ne les signale ; c’est de la typographie ordinaire, quoi que ce soit qui les ait produits.

Deux raisons de faire la vérification méritent d’être distinguées. La quotidienne : les caractères invisibles cassent les choses en silence — une comparaison qui devrait réussir, une clé de recherche, un champ qui refuse votre saisie sans dire pourquoi. L’autre : le texte est lu par des programmes autant que par des gens — les caractères du bloc Tags ont été prévus pour des étiquettes de langue, ils ne dessinent rien dans la plupart des éditeurs et des diffs, et ce qui lit le texte au lieu de le regarder les lit quand même. Si vous collez une longue réponse entière, notez que la liste des lignes est rognée et que le compte ne l’est pas : il porte sur tout ce que vous avez collé.

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.
Le texte d’un modèle d’IA contient-il des caractères invisibles ?
Parfois, et jamais d’une façon qui identifie quoi que ce soit. Le cas le plus commenté fut une espace insécable étroite (U+202F) apparaissant dans de longues réponses là où une espace ordinaire avait sa place ; le constructeur du modèle a répondu qu’il s’agissait d’un sous-produit de l’entraînement et non de quelque chose de placé là exprès, et on a cessé de la voir en quelques jours. Collez votre texte ici et vous verrez ce qu’il contient. Ce que vous ne verrez pas, c’est d’où il vient, parce qu’un point de code n’enregistre pas cela.
Comment trouver et supprimer les caractères cachés de mon texte ?
Collez-le ici pour les trouver : chaque point de code reçoit une ligne, avec son nom s’il en a un, et ceux qui méritent un second regard sont comptés au-dessus de la liste. Les supprimer est le travail de votre éditeur — rien ici ne réécrit votre texte — mais une fois que vous savez quel point de code vous cherchez, chaque ligne le donne en échappement dans six formats, ce qu’attend un champ de recherche qui comprend les échappements.
Un tiret cadratin ou une apostrophe typographique est-il un caractère caché ?
Non. Le tiret cadratin (U+2014) et l’apostrophe typographique (U+2019) dessinent tous deux une marque à l’écran, donc rien ici ne les signale. On les présente souvent comme l’indice d’une écriture générée, ce qui est une question de style à laquelle un inspecteur de caractères ne peut pas répondre. Ils valent tout de même d’être connus pour une autre raison : ni l’un ni l’autre n’est de l’ASCII, tous deux peuvent donc gêner ce qui en attend, et une chaîne écrite avec eux n’est pas égale à la même chaîne tapée avec un trait d’union et une apostrophe droite.

Outils connexes