Inspetor de caracteres Unicode

Decompõe o texto nos seus pontos de código com nomes, categorias, blocos e codificações, e assinala caracteres invisíveis, bidi e sósias.

Entrada

O que esta ferramenta faz

Cole qualquer texto e ele volta decomposto nos caracteres individuais de que é realmente feito. Cada um recebe o seu nome Unicode, o seu número U+, que espécie de caractere é, a que sistema de escrita e a que bloco pertence, os bytes que ocupa em UTF-8 e UTF-16, e a sequência de escape que exige em seis formatos diferentes.

A maioria das pessoas chega aqui porque alguma coisa não bate certo. Uma cadeia é mais longa do que parece, dois valores que aparecem idênticos recusam-se a ser iguais, um nome de usuário foi rejeitado por causa de caracteres que parecem letras normais, ou uma linha de código lê-se de forma diferente da que se comporta. As quatro têm a mesma forma: um caractere fazendo algo que a tela não mostra.

Caracteres, pontos de código e grafemas

A palavra «caractere» significa pelo menos três coisas diferentes, e confundi-las é onde começa quase toda a confusão à volta do Unicode. Um ponto de código é um valor do padrão Unicode, escrito U+0041 ou U+1F600. Um grafema é o que quem lê conta como um caractere. Muitas vezes não são o mesmo número.

  • Um «a» simples é 1 grafema, 1 ponto de código e 1 unidade UTF-16 — tudo coincide.
  • U+00E9, o é pré-composto, é 1 grafema, 1 ponto de código e 1 unidade UTF-16.
  • U+0065 U+0301, a mesma letra escrita como e mais um acento combinante, é 1 grafema mas 2 pontos de código e 2 unidades UTF-16.
  • U+1F600, a cara sorridente, é 1 grafema e 1 ponto de código mas 2 unidades UTF-16 — vive acima do BMP, por isso o JavaScript guarda-o como par substituto.
  • O emoji de família é 1 grafema, 5 pontos de código e 8 unidades UTF-16: três pessoas unidas por dois unificadores invisíveis.

O emoji de família é o caso que pega as pessoas. Parece uma coisa só, o JavaScript relata comprimento 8, e uma coluna de banco de dados de 5 caracteres não o suporta. Esta ferramenta agrupa os pontos de código sob o grafema que constroem, para que ambas as leituras estejam visíveis ao mesmo tempo em vez de terem de ser conciliadas de cabeça.

Os caracteres que não se conseguem ver

Um número surpreendente de caracteres Unicode não desenha absolutamente nada. Existem para controlar como o texto à sua volta se comporta e, como não deixam marca, sobrevivem a copiar, colar e rever sem que ninguém repare. São estes que a ferramenta aponta em vez de se limitar a listar:

  • Espaço, unificador e não-unificador de largura zero (U+200B, U+200D, U+200C) — sem largura, sem marca, e quebram toda a comparação de cadeias em que tocam.
  • Hífen opcional (U+00AD) — uma sugestão de onde uma palavra pode partir, invisível até acontecer.
  • Espaço inquebrável (U+00A0) — parece exatamente um espaço e é outro caractere, e é por isso que sobrevive tanto tempo em arquivos de configuração.
  • Marca de ordem de bytes (U+FEFF) — fica muitas vezes no início de um arquivo, onde se torna um primeiro caractere invisível do primeiro valor.
  • Controles e imposições bidi (de U+202A a U+202E, de U+2066 a U+2069) — reordenam o texto à sua volta.

Os controles bidi merecem menção à parte. Foram acrescentados para que o hebraico e o árabe possam misturar-se corretamente com texto latino, e desempenham bem essa função. Mas uma imposição colocada dentro de um comentário ou de uma cadeia pode fazer com que uma linha de código apareça numa ordem completamente diferente da que o compilador lê — o que significa que um revisor pode aprovar código que faz outra coisa. Essa classe de truque é conhecida por Trojan Source, e é a razão por que esta ferramenta assinala cada controle bidi separadamente dos restantes caracteres invisíveis.

Caracteres sósias e escritas misturadas

O Unicode contém muitos caracteres visualmente idênticos a letras ASCII que não o são. O а cirílico, o ο grego e o a latino ocupam pontos de código diferentes e desenham-se da mesma maneira na maioria das fontes. Um domínio ou um nome de usuário construído com eles parece correto e aponta para outro lugar.

Assinalar cada sósia não-ASCII seria inútil: pintaria de vermelho cada palavra russa, grega ou hebraica, e um aviso que dispara com texto normal é um aviso que as pessoas aprendem a saltar. Por isso a regra aqui é a do padrão de segurança do Unicode — uma palavra é suspeita quando as letras lá dentro vêm de mais do que um sistema de escrita:

  • «paypal.com» escrito inteiramente em letras latinas: não há nada a dizer.
  • A mesma palavra com um er e um a cirílicos à frente do «ypal» latino: uma palavra, dois sistemas de escrita, assinalada.
  • Uma palavra russa inteira em cirílico: um só sistema de escrita, texto comum, não assinalada.
  • Uma frase japonesa que usa han, hiragana e katakana em conjunto: três escritas, um só sistema de escrita, não assinalada.

Este último caso é a razão por que a regra exige cuidado. O japonês escreve-se com três escritas ao mesmo tempo e o coreano mistura duas, portanto um teste ingênuo de «mais do que uma escrita é suspeito» declararia ataque cada frase japonesa. As combinações que são genuinamente um só sistema de escrita são tratadas como uma, e é isso que mantém o aviso com sentido.

Normalização, ou porque duas cadeias idênticas diferem

Alguns caracteres podem escrever-se de mais do que uma maneira. A letra é ou o único ponto de código U+00E9 ou o par U+0065 U+0301 — um e simples seguido de um acento agudo combinante. Ambas se mostram de forma idêntica. Nenhuma está errada. Iguais não são.

A normalização é o processo de escolher uma grafia, e tem quatro formas. A NFC compõe sempre que pode e é o que a web assume por padrão; a NFD decompõe em vez disso. As duas formas K vão mais longe e dobram também os caracteres de compatibilidade — o A de largura inteira passa a um A simples, e a ligadura fi passa a fi — o que é útil para pesquisa e correspondência e com perdas para armazenamento.

O painel aqui mostra as quatro formas do que colar e marca quais delas diferem da entrada. Se nenhuma diferir, o texto já está normalizado e a discrepância que anda perseguindo está em outro lugar. Se algumas diferirem, encontrou-a.

Nomes, categorias, escritas e blocos

Cada caractere atribuído carrega quatro elementos de identidade, e respondem a perguntas diferentes. O nome é o próprio caractere. A categoria geral diz que espécie de coisa é — uma letra minúscula, uma marca combinante, um símbolo de moeda, um controle de formato. A escrita é o sistema de escrita a que pertence. O bloco é o intervalo de pontos de código em que foi atribuído, o que é um fato organizativo e não linguístico.

Bloco e escrita confundem-se com facilidade e muitas vezes divergem. As letras latinas vivem em pelo menos meia dúzia de blocos, e um bloco pode conter caracteres de várias escritas. Quando quer saber «que alfabeto é este», a resposta é a escrita; quando quer saber «onde é que isto vive no padrão», é o bloco.

Os nomes, as escritas e os blocos são mostrados aqui em inglês em todas as línguas, deliberadamente. São identificadores definidos pelo padrão, não prosa — U+200D chama-se ZERO WIDTH JOINER no mundo inteiro, e traduzi-lo tornaria impossível procurá-lo.

Codificações e escapes

Um ponto de código é um número; só se torna bytes depois de escolhida uma codificação. O UTF-8 usa entre um e quatro bytes e deixa o ASCII inalterado, e foi por isso que venceu. O UTF-16 usa uma ou duas unidades de 16 bits, e é disso que são feitas as cadeias do JavaScript — razão por que o .length relata 2 para um único emoji.

U+0041   41              # UTF-8: um byte, igual ao ASCII
U+00E9   C3 A9           # UTF-8: dois bytes
U+20AC   E2 82 AC        # UTF-8: três bytes
U+1F600  F0 9F 98 80     # UTF-8: quatro bytes

Cada linha aqui dá também o caractere escrito como escape em JavaScript, JSON, HTML, CSS, URL e Python, pronto a copiar. Não são todos iguais, e acima do BMP a diferença conta: o JavaScript e o Python têm um escape por ponto de código e usam-no, ao passo que o JSON não tem nenhum e tem de soletrar um emoji como as suas duas metades substitutas.

Caracteres ocultos, e o texto que saiu de um modelo de IA

Boa parte do texto que as pessoas colam aqui não foi digitado por elas: veio de um documento, de uma página web, de um chamado ou da resposta de um assistente, e a dúvida com que chegam é se ele carrega caracteres que não conseguem ver. É exatamente essa a pergunta que esta ferramenta responde, e vale dizer até onde vai a resposta: o texto é decomposto nos seus pontos de código, cada um com o seu nome quando o padrão lhe dá um, e os que merecem uma segunda olhada são contados numa linha acima da lista. Se essa linha não aparece, nada foi encontrado.

O que um caractere oculto prova é mais estreito do que sugere quase tudo o que se escreve sobre o assunto. Nada dentro de um ponto de código registra de onde ele veio: o que entrou ao copiar de um gerenciador de conteúdo e o que entrou com uma resposta gerada são o mesmo valor. Caracteres invisíveis foram mesmo relatados na saída de grandes modelos de linguagem e, no caso que recebeu mais atenção — um espaço inquebrável estreito onde cabia um espaço comum —, o fabricante do modelo respondeu que era um subproduto do treinamento e não algo colocado ali de propósito, e os relatos cessaram em poucos dias. Um achado aqui diz, então, o que apagar. Não diz o que escreveu a frase.

  • Espaços que não são o espaço comum. O espaço inquebrável (U+00A0) e o espaço inquebrável estreito (U+202F) são os dois mais citados; cada um tem mais ou menos a largura de um espaço, cada um é outro caractere, e esta ferramenta assinala os dois.
  • Caracteres sem largura nenhuma. O espaço de largura zero (U+200B), o unificador de palavras (U+2060) e a marca de ordem de bytes (U+FEFF) não desenham nada, sobrevivem à revisão e quebram a primeira comparação em que tocam. São assinalados como invisíveis.
  • O bloco Tags (de U+E0000 a U+E007F). Ele guarda um ponto de código invisível para cada caractere ASCII imprimível — U+E0041 se chama TAG LATIN CAPITAL LETTER A — de modo que uma frase inteira pode ser soletrada dentro de um texto que não mostra absolutamente nada. Cada um é nomeado aqui e assinalado como invisível.
  • A pontuação de que se reclama e que não está oculta. O travessão (U+2014) e o apóstrofo tipográfico (U+2019) desenham uma marca visível, então aqui nada os assinala; são tipografia comum, seja lá o que os tenha produzido.

Vale separar dois motivos para fazer a verificação. O cotidiano é que caracteres invisíveis quebram coisas em silêncio: uma comparação que deveria bater, uma chave de busca, um campo que recusa o que você digitou sem dizer por quê. O outro é que o texto é lido por programas tanto quanto por pessoas: os caracteres do bloco Tags foram pensados para etiquetas de idioma, não desenham nada na maioria dos editores e diffs, e o que lê o texto em vez de olhar para ele os lê do mesmo jeito. Se você colar uma resposta longa inteira, note que a lista de linhas é cortada e a contagem não: ela é tirada sobre tudo o que você colou.

Perguntas frequentes

Por que a minha cadeia é mais longa do que o número de caracteres que vejo?
Ou contém caracteres invisíveis, ou contém grafemas construídos a partir de vários pontos de código. Ambos são mostrados aqui: os caracteres invisíveis são assinalados, e os grafemas de vários pontos de código são agrupados para que veja o único caractere e os vários valores de que é feito.
Duas cadeias parecem idênticas e não são iguais. Por quê?
Normalmente normalização. Uma letra acentuada pode ser um ponto de código ou uma letra mais uma marca combinante, e ambas se desenham igual. Cole aqui cada cadeia e compare as listas de pontos de código, ou veja que formas de normalização diferem da entrada.
O que é um espaço de largura zero e como foi parar ao meu texto?
É um caractere sem largura e sem marca, usado para sugerir uma oportunidade de mudança de linha. Chega normalmente ao copiar texto de uma página web, de um editor de documentos ou de um cliente de conversação e, como nada é mostrado, sobrevive a todas as revisões até que algo compare cadeias e falhe.
Qual é a diferença entre um ponto de código e um caractere?
Um ponto de código é um valor Unicode. Um caractere no sentido corrente é um grafema — o que quem lê conta como um — e um grafema pode valer vários pontos de código. Um emoji de família são cinco pontos de código e um grafema.
Por que a ferramenta diz que alguns caracteres não têm nome?
Por quatro razões diferentes, e diz qual. Os substitutos e os pontos de código de uso privado não têm nome no padrão, os não atribuídos não têm nada para nomear, e um caractere de um bloco histórico raramente consultado tem um nome que esta versão não transporta: a tabela de nomes traz os blocos de onde as pessoas realmente colam em vez de os enviar todos.
Um aviso de escritas misturadas é prova de um ataque?
Não. Significa que uma única palavra vai buscar as suas letras a mais do que um sistema de escrita, que é a forma de um nome forjado e também algo que o texto comum faz de vez em quando. É um motivo para olhar, não um veredicto.
Alguma coisa do que colo é enviada para um servidor?
Não. O banco de dados de caracteres é baixado com a página e tudo roda no seu navegador; nada do que escreve é carregado ou registrado, e funciona sem conexão de rede.
O texto de um modelo de IA contém caracteres invisíveis?
Às vezes, e nunca de um jeito que identifique alguma coisa. O caso que recebeu mais atenção foi um espaço inquebrável estreito (U+202F) que aparecia em respostas longas onde cabia um espaço comum; o fabricante do modelo respondeu que era um subproduto do treinamento e não algo colocado ali de propósito, e em poucos dias ele deixou de ser visto. Cole seu texto aqui e você verá o que há nele. O que você não verá é de onde ele veio, porque um ponto de código não registra isso.
Como encontro e removo caracteres ocultos no meu texto?
Cole aqui para encontrá-los: cada ponto de código ganha uma linha, com o seu nome se tiver um, e os que merecem uma segunda olhada são contados acima da lista. Removê-los é tarefa do seu editor — aqui nada reescreve o seu texto — mas, uma vez que você sabe qual ponto de código está perseguindo, cada linha o dá como escape em seis formatos, que é o que uma caixa de busca capaz de lê-los espera.
Um travessão ou uma aspa curva é um caractere oculto?
Não. O travessão (U+2014) e o apóstrofo tipográfico (U+2019) desenham uma marca na tela, então aqui nada os assinala. São muitas vezes apontados como indício de escrita gerada, o que é uma questão de estilo a que um inspetor de caracteres não consegue responder. Ainda assim vale conhecê-los por outro motivo: nenhum dos dois é ASCII, então ambos podem incomodar algo que espera ASCII, e uma cadeia escrita com eles não fica igual à mesma cadeia digitada com hífen e apóstrofo reto.

Ferramentas relacionadas