O que conta como um caractere?
Quatro sistemas diferentes contam o mesmo texto de quatro jeitos diferentes. Em texto comum em inglês eles concordam, e é por isso que o problema fica invisível até o dia em que deixa de ficar.
Última revisão:
Um emoji, quatro respostas
Pegue um joinha com tom de pele aplicado, ou o emoji de uma família. Conte de quatro jeitos e você terá quatro números, e nenhum deles está errado.
Quem lê vê um caractere. Uma coisa só, que se apaga com um toque na tecla de apagar.
Uma expressão regular pode ver sete. Ele é formado por vários pontos de código ligados por conectores invisíveis — um emoji base, um modificador, um conector, outro emoji, e assim por diante.
O JavaScript informa onze. Ele guarda texto em unidades de 16 bits, e tudo o que está fora da faixa básica ocupa duas delas, então cada um desses pontos de código pode contar em dobro.
Uma coluna de banco de dados vê vinte e cinco bytes. Codificado em UTF-8 para armazenamento, cada ponto de código ocupa até quatro bytes.
Em texto comum em inglês, os quatro números são idênticos. É exatamente por isso que o problema fica invisível até alguém digitar um nome com acento, uma frase em japonês ou um único emoji — e um campo que funcionou por três anos começa a cortar dados.
Qual número o seu limite usa
A pergunta prática nunca é “qual o tamanho deste texto”. É “o que está impondo o limite”, e há quatro respostas comuns.
Uma coluna de banco de dados declarada como VARCHAR conta bytes na maioria dos sistemas. É por isso que um campo que aceita tranquilamente 255 caracteres em inglês rejeita bem menos caracteres japoneses — três bytes cada — e por isso um nome com acento às vezes não cabe quando a versão sem acento cabia.
Um SMS tem 160 caracteres no seu próprio alfabeto de sete bits, e cai para 70 por mensagem assim que um caractere fica fora dele. Uma única aspa curva colada de um editor de texto, ou um emoji, pode transformar uma mensagem de uma parte em três e triplicar o que você paga.
Um validador de formulário em JavaScript quase sempre conta unidades UTF-16, e é por isso que um campo que diz 280 caracteres pode rejeitar um texto que parece bem menor quando está cheio de emojis.
Uma pessoa conta o que consegue ver, a única definição que importa para um título, uma tag title ou qualquer coisa com um limite visual.
Onde as contagens divergem
Saber quais entradas causam problema é mais útil do que saber a teoria, porque essas entradas são previsíveis.
Emojis, principalmente os compostos — tons de pele, famílias, bandeiras, profissões. Uma bandeira são duas letras de indicador regional; uma profissão geralmente é uma pessoa, um conector e um objeto.
Caracteres acentuados e combinantes. Um é pode ser escrito como um único ponto de código ou como um e seguido de um acento agudo combinante. Eles parecem idênticos, são ordenados de forma diferente, são considerados diferentes numa comparação e contam de forma diferente — e as duas formas aparecem em dados reais, muitas vezes na mesma coluna.
Escritas não latinas. Caracteres chineses, japoneses e coreanos têm três bytes cada em UTF-8. Hindi, tailandês, tâmil e árabe formam agrupamentos que o leitor percebe como uma unidade e que têm vários pontos de código.
Símbolos matemáticos e musicais, que ficam fora da faixa básica e por isso contam em dobro em UTF-16.
O que fazer
Conte o que o seu limite conta. Um contador que mostra os quatro números responde à pergunta direto, o que é mais rápido do que raciocinar sobre ela. No código, use a função certa: Intl.Segmenter no JavaScript conta o que o leitor vê, len(s.encode("utf-8")) no Python conta bytes, e no PHP use mb_strlen em vez de strlen.
Corrija a coluna, não o formulário. Se o limite vem de um campo do banco de dados, a solução duradoura está na origem: declare-o como utf8mb4 no MySQL ou text no PostgreSQL. O antigo “utf8” do MySQL é famoso por ter só três bytes por caractere e não conseguir guardar nenhum emoji — uma armadilha antiga que já cortou silenciosamente uma quantidade enorme de dados reais. Contar com cuidado no formulário é um remendo para uma coluna que deveria ter sido declarada de outro jeito.
Normalize na entrada. Converter o texto para uma única forma normal no momento em que ele entra faz as duas grafias de é virarem uma só, e comparações, ordenação e contagens passam a concordar. Toda linguagem tem uma função para isso, e fazer uma vez na fronteira é muito mais fácil do que lidar com as duas formas em todo lugar.
Um caso relacionado: Base64
Vale mencionar porque causa o mesmo tipo de surpresa. Codificar dados em Base64 pega três bytes por vez e os escreve como quatro caracteres, então o resultado fica cerca de um terço maior — é aritmética, não ineficiência. Um anexo de 6 MB vira cerca de 8 MB de texto, e é por isso que um arquivo bem abaixo de um limite de tamanho pode ser rejeitado depois de codificado para envio.
A mesma conta explica por que anexos de e-mail voltam em limites que aparentemente respeitam. Se você está comparando algo com um teto, compare a forma codificada.
Perguntas frequentes
Por que meu campo de 255 caracteres rejeita um texto mais curto?
Quase certamente porque ele conta bytes, e não caracteres. Letras acentuadas ocupam dois bytes em UTF-8 e caracteres CJK ocupam três, então 255 bytes podem ser 85 caracteres japoneses. Declarar a coluna como utf8mb4 ou text resolve de verdade.
Um emoji pode mesmo me custar três SMS?
Pode. Uma mensagem só com caracteres do alfabeto GSM tem 160 por parte; um caractere fora dele muda a mensagem inteira para uma codificação de 70 caracteres. Então uma mensagem de 150 caracteres com um emoji vira três partes em vez de uma.
Qual contagem devo usar para uma tag title?
Nenhuma delas com precisão — os buscadores cortam pela largura em pixels, não pelo número de caracteres, então um título com maiúsculas largas é cortado antes de um com minúsculas estreitas. Cerca de 60 caracteres é a regra prática de costume, e é uma referência, não um limite.
Por que dois textos que parecem idênticos não batem?
Normalmente porque um caractere acentuado foi escrito de dois jeitos diferentes — como um único ponto de código em um e como uma letra base mais uma marca combinante no outro. Normalizar os dois para a mesma forma antes de comparar faz com que batam, e vale a pena fazer isso no ponto em que os dados entram no seu sistema.
Uma contagem de palavras é mais estável que uma de caracteres?
Em inglês, quase sempre. Não em todas as línguas: chinês e japonês não separam palavras com espaços, e o tailandês também não, então contar palavras nessas escritas exige segmentação, e ferramentas diferentes dão respostas diferentes.
Vale a pena ler também
PDF grande demais para o e-mail
Os três motivos de um PDF ser grande, e qual solução serve para o seu.
Qual formato de imagem?
Uma pergunta decide. JPG, PNG, WebP e AVIF comparados com honestidade.
PDFs escaneados e OCR
Por que uma digitalização não tem texto, o que o OCR acrescenta e a configuração que decide a precisão.