Caracteres, code points e bytes

O Unicode atribui a cada caractere de cada escrita um número, o code point, escrito como U+ seguido de dígitos hexadecimais: A é U+0041, ç é U+00E7, o euro U+20AC, o rosto sorridente U+1F600. São mais de um milhão de posições, cerca de 150.000 já em uso.

Depois o code point precisa virar bytes. O UTF-8, a codificação da web, usa de um a quatro bytes: um para o ASCII, dois para letras acentuadas, três para quase todo o resto e quatro para emoji e símbolos raros. O UTF-16, usado por JavaScript, Java e Windows, usa dois bytes ou um par substituto de quatro.

Por isso o comprimento de uma string depende do que se conta: "😀".length vale 2 em JavaScript, o caractere ocupa 4 bytes em UTF-8 e para o olho é um só. E uma letra acentuada pode ser um único code point ou uma letra mais um acento combinante: parecem iguais, mas não são.

Erros comuns

  • Cortar uma string UTF-8 num número fixo de bytes: um caractere pode ser partido ao meio.
  • Contar caracteres com .length em JavaScript: emoji e caracteres fora do plano básico contam 2.
  • Comparar textos sem normalizá-los: um é pré-composto e um e com acento combinante são diferentes para o computador.

Perguntas frequentes

Qual a diferença entre Unicode e UTF-8?

Unicode é a lista de caracteres com seus números; UTF-8 é um dos jeitos de escrever esses números em bytes. O mesmo texto Unicode pode ser salvo em UTF-8, UTF-16 ou UTF-32.

Por que vejo caracteres estranhos como ç no lugar de ç?

Porque um texto em UTF-8 está sendo lido como Latin-1: os dois bytes do ç, C3 A7, viram dois caracteres separados. Basta declarar a codificação certa.

Como escrevo um caractere Unicode em HTML?

Com uma entidade numérica: ç em decimal ou ç em hexadecimal. Se a página estiver em UTF-8, dá para digitar o caractere diretamente.

Como funciona este cálculo

UTF-8: até U+007F um byte 0xxxxxxx; até U+07FF dois bytes 110xxxxx 10xxxxxx; até U+FFFF três bytes 1110xxxx 10xxxxxx 10xxxxxx; acima, quatro bytes 11110xxx e três de continuação. UTF-16: até U+FFFF uma unidade de 16 bits; acima, subtrai-se 0x10000 e os 20 bits se dividem em dois substitutos, 0xD800 + os 10 bits altos e 0xDC00 + os 10 baixos.