Texto

Os caracteres invisíveis que quebram seu texto

Um espaço que não é um espaço, um caractere sem largura, uma marca no começo de um arquivo. De onde eles vêm e como tirá-los.

Um caractere invisível é um caractere de verdade que aparece em branco, ou não aparece de jeito nenhum. Os suspeitos de sempre são U+00A0, o espaço não separável; U+200B, o espaço de largura zero; U+FEFF, a marca de ordem de bytes; e os onze espaços tipográficos que ficam entre U+2000 e U+200A. Seus olhos pulam por cima deles. Toda comparação de strings que um computador faz leva todos em conta. É por isso que uma busca numa planilha falha num nome que você lê sem dificuldade nenhuma, que uma senha colada é recusada, e que duas linhas de aparência idêntica se recusam a virar uma só.

O sintoma tem sempre o mesmo formato: duas coisas que parecem iguais não são iguais. Não há nada de errado com o software, nem com os seus olhos. Tem um caractere a mais ali dentro e ele não tem desenho.

Quais caracteres são invisíveis?

Dois grupos, com causas diferentes.

Espaços que não são a barra de espaço

A categoria de separadores de espaço do Unicode tem 17 caracteres. Exatamente um deles é a tecla do seu teclado. Os outros 16 parecem um espaço, medem como um espaço e não batem com um espaço.

Caracteres sem largura nenhuma

Esses não parecem nem um espaço. Ocupam zero pixel, então um texto que contém um deles é visualmente idêntico ao texto sem ele.

Como eu vejo os caracteres invisíveis?

Você não vê, não diretamente. Dá para detectar de três jeitos.

Conte. Cole o texto no limpador de espaços daqui e ele relata o que encontrou antes de mudar qualquer coisa: quantos espaços não separáveis, quantos outros espaços Unicode, quantos caracteres de largura zero, quantas linhas terminam em espaço. Um relatório vazio descarta a categoria inteira em dez segundos.

Compare o tamanho com o que você espera. Um código de produto que deveria ter 8 caracteres e informa 9 está levando um passageiro.

Ligue o destaque do seu editor. O VS Code marca com um quadro os caracteres invisíveis e ambíguos por padrão, um recurso que ele acrescentou depois que a divulgação do Trojan Source em 2021 mostrou que dá para fazer um código-fonte ser lido de um jeito por uma pessoa e de outro por um compilador. Quase todo editor tem uma versão disso, em “mostrar espaços em branco” ou “exibir caracteres de controle”.

Por que localizar e substituir não remove esses caracteres?

Porque os atalhos que você usaria discordam sobre o que conta como espaço em branco.

Em JavaScript, \s e trim() batem com U+00A0 e U+FEFF, mas não com U+200B, que é classificado como caractere de formato e não como espaço. O Python se comporta do mesmo jeito: '\xa0'.isspace() é verdadeiro, '\u200b'.isspace() é falso, então o .strip() deixa o caractere lá. O Excel é pior. TRIM remove só o espaço ASCII, o caractere 32, e CLEAN só os caracteres de 0 a 31, então nenhuma das duas encosta no espaço não separável, que é o 160. Ali você precisa de SUBSTITUTE(A1,CHAR(160)," ") antes de TRIM servir para alguma coisa.

Para mirar neles explicitamente, use uma regex. Marque Expressão regular na ferramenta de localizar e substituir e procure por [\u00A0\u200B\u200C\u200D\u2060\uFEFF] — o conjunto de largura zero mais o espaço não separável. Leia a contagem de ocorrências antes de substituir qualquer coisa.

De onde eles vêm?

Quase sempre de um copiar e colar que cruza uma fronteira: uma página web, onde cada   é copiado como um U+00A0 de verdade; um documento do Word, onde a autoformatação acrescenta esses caracteres em volta de números e iniciais; um PDF, que carrega o espaçamento que quem diagramou usou.

A marca de ordem de bytes é a exceção, e ela quebra parsers em vez de comparações. A exportação de CSV em UTF-8 do Excel coloca uma na frente do arquivo. O JSON.parse dá erro com um U+FEFF no começo, já que o JSON só aceita espaço, tab, retorno de carro e avanço de linha como espaço em branco. O módulo csv do Python te entrega um primeiro campo chamado \ufeffid a não ser que você abra o arquivo com a codificação utf-8-sig, que existe exatamente para isso. Se uma conversão de CSV produzir uma chave em que você não consegue ver nada de errado, confira isso primeiro.

Como eu removo esses caracteres?

Em prosa, troque cada espaço exótico por um comum e apague os caracteres de largura zero de vez. É o que o limpador faz por padrão: “Substituir espaços exóticos” cobre os 16 separadores de espaço fora do padrão, “Remover caracteres invisíveis” cobre o conjunto de largura zero e a marca de ordem de bytes, e as contagens de antes e depois te dizem quantos foram embora.

Em código, desligue antes o aparo das linhas e a junção dos espaços repetidos. A indentação é sintaxe em Python, YAML e Makefiles, e dois espaços no fim da linha são uma quebra de linha forçada em Markdown. Remover os caracteres de largura zero continua seguro em todos esses casos, e é a opção de que você realmente precisa — um espaço de largura zero colado num arquivo-fonte gera um erro que ninguém consegue enxergar lendo.

Em listas, limpe antes de remover duplicatas, não depois: duas entradas que diferem por um espaço não separável no fim são duas strings diferentes para qualquer rotina de remoção de duplicatas já escrita. Remover linhas duplicadas de uma lista cobre a outra metade desse problema, que é decidir se maiúsculas e ordem das palavras contam como diferença.

Quando é melhor deixar esses caracteres em paz?

Apagar todos de uma vez está errado pelo menos com a mesma frequência com que está certo.

Esse é o limite honesto de toda ferramenta dessa categoria: ela acha caracteres, não consegue ler intenção.

O que um limpador de espaços não vai pegar

Vários problemas de caractere invisível parecem idênticos por fora e precisam de outra solução:

Os quatro têm a mesma assinatura de um espaço invisível, e nenhum deles responde ao mesmo tratamento. Descobrir qual deles você tem é a maior parte do trabalho.

O limpador de espaços lista o que encontrou por tipo antes de mexer em qualquer coisa, e essa é a parte que vale a pena usar: saber que o texto contém quatro espaços não separáveis e uma marca de ordem de bytes te diz mais do que uma versão limpa sem explicação nenhuma. Ele roda dentro da página, então nada do que você colar é enviado para lugar nenhum.

Se você chegou aqui porque uma busca não achava uma palavra que está visivelmente no documento, o caractere invisível é só um dos motivos para isso acontecer. Localizar e substituir: os erros que te custam uma hora passa pelos outros, incluindo as aspas tipográficas e a troca em dois sentidos que come as duas palavras.

Perguntas frequentes

O que são caracteres invisíveis em um texto?

São caracteres Unicode que aparecem em branco ou não aparecem de jeito nenhum, como o espaço não separável (U+00A0), o espaço de largura zero (U+200B) e a marca de ordem de bytes (U+FEFF). Eles ocupam uma posição na string e contam no tamanho dela, então um computador trata um texto que contém um deles como diferente do texto sem ele, mesmo que os dois pareçam idênticos.

Como eu encontro caracteres invisíveis em uma string?

Cole a string numa ferramenta que os conte por tipo, ou compare a contagem de caracteres com o tamanho que você espera. A maioria dos editores de código também consegue destacá-los: o VS Code marca com um quadro os caracteres invisíveis e ambíguos por padrão. Uma inspeção visual simples nunca vai funcionar, e esse é o problema inteiro.

Por que a minha busca não acha uma palavra que está claramente ali?

Normalmente porque um dos espaços dela não é um espaço comum. Um espaço não separável é idêntico pixel a pixel a um normal e não bate com ele, então buscar por "New York" falha num texto que contém "New" mais U+00A0 mais "York". Aspas tipográficas copiadas do Word causam a mesma falha silenciosa.

Por que o TRIM não remove os espaços da minha planilha?

O TRIM do Excel só remove o caractere 32, o espaço ASCII, e o CLEAN só remove os caracteres de 0 a 31. O espaço não separável é o caractere 160, então nenhuma das duas funções encosta nele. Envolva a célula em SUBSTITUTE(A1,CHAR(160)," ") primeiro, e aí o TRIM vai funcionar como esperado.

É seguro remover todos os caracteres de largura zero?

Nem sempre. O unificador de largura zero é o que mantém as sequências de emojis juntas, então tirá-lo transforma um emoji de família em várias pessoas separadas. O não unificador de largura zero tem valor gramatical em persa e na escrita árabe. Em prosa simples em inglês, removê-los é seguro e costuma ser a decisão certa.

O que é uma marca de ordem de bytes e por que ela está quebrando meu arquivo?

É o U+FEFF, três bytes no começo de um arquivo UTF-8 que anunciam a codificação. Muitos parsers não esperam por ela: o JSON.parse dá erro quando ela está no começo, e um leitor de CSV vai informar o cabeçalho da primeira coluna como um nome desconhecido. O Python resolve isso com a codificação utf-8-sig, que remove a marca durante a leitura.

Última atualização 19 de setembro de 2026