O dá a cada caractere um número. Uma forma de codificação transforma o número em unidades de código, e um esquema de codificação põe as unidades em bytes. Unicode: pontos de código, planos, nomes e propriedades trata dos números; este artigo trata dos bytes: o que cada forma escreve, como lê-la de volta num dump hexadecimal e o que um decodificador faz quando os bytes estão errados. O conversor de charsets mostra tudo isso para qualquer texto ou sequência de bytes.

UTF-8: de um a quatro bytes, e o primeiro diz quantos

A 3629 apresenta o como uma tabela. Os bits de um ponto de código se espalham por um a quatro bytes, e os bits altos de cada byte dizem que tipo de byte ele é:

Pontos de códigoBytes (cada x é um bit do ponto de código)
U+0000 a U+007F0xxxxxxx
U+0080 a U+07FF110xxxxx 10xxxxxx
U+0800 a U+FFFF1110xxxx 10xxxxxx 10xxxxxx
U+10000 a U+10FFFF11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

Tome o é, U+00E9. Os seus onze bits são 00011 101001, então ele vira 110 00011 e 10 101001: C3 A9. O símbolo do euro, U+20AC, ocupa três bytes, E2 82 AC, e o rosto sorridente, U+1F600, quatro, F0 9F 98 80.

Daí saem três propriedades, e a RFC 3629 as lista. Texto já é UTF-8, byte por byte. "US-ASCII octet values do not appear otherwise" (valores de bytes US-ASCII não aparecem de outra forma), então procurar / ou " em UTF-8 nunca encontra parte de um caractere mais longo. E o primeiro byte de uma sequência dá o seu comprimento, então quem cai em qualquer ponto do fluxo encontra onde começa o próximo caractere.

Como ler UTF-8 num dump hexadecimal

O primeiro byte de um caractere diz o que você está vendo:

Primeiro byteSignificado
00 a 7Fum caractere ASCII, um byte
a o início de um caractere de dois bytes
E0 a EFo início de um caractere de três bytes
F0 a F4o início de um caractere de quatro bytes
80 a BFnunca é início: continua o caractere anterior
C0, C1, F5 a FFnunca é nada

A RFC 3629 resume a última linha numa frase: "The octet values C0, C1, F5 to FF never appear." C0 e C1 só poderiam começar uma forma de dois bytes de um caractere ASCII, que já tem uma forma de um byte. F5 e acima começariam pontos de código além de U+10FFFF, onde o Unicode termina.

Quatro primeiros bytes também restringem o byte seguinte. A Tabela 3-7 do padrão Unicode lista toda sequência bem formada, e as únicas exceções a "80 a BF" estão no segundo byte:

Depois deO byte seguinte precisa serPorque qualquer outro seria
E0A0 a BFuma forma mais longa de um ponto de código abaixo de U+0800
ED80 a 9Fum surrogate, U+D800 a U+DFFF
F090 a BFuma forma mais longa de um ponto de código abaixo de U+10000
F480 a 8Fum ponto de código além de U+10FFFF

Quando os bytes estão errados

Um decodificador que encontra uma sequência malformada precisa decidir quanto dela substituir, e os decodificadores nem sempre decidiram igual. O padrão Unicode descreve uma prática na seção 3.9.6, observando que "an increasing number of implementations are adopting" (um número crescente de implementações a está adotando), e o WHATWG Encoding Standard faz dela o único comportamento que um navegador pode ter: um U+FFFD REPLACEMENT CHARACTER para cada subparte máxima, a maior sequência de bytes que ainda poderia ter sido o início de um caractere bem formado, ou um único byte quando nada poderia.

Os próprios exemplos do padrão Unicode mostram o que isso significa, e cada um é um exemplo pronto no conversor:

BytesDecodificadoPor quê
C0 AF E0 80 BF F0 81 82 41oito U+FFFD, depois Acada byte de uma forma mais longa que o necessário é substituído sozinho (Tabela 3-8)
ED A0 80 ED BF BF ED AF 41oito U+FFFD, depois Ao mesmo para surrogates (Tabela 3-9)
F4 91 92 93 FF 41 80 BF 42cinco U+FFFD, A, dois U+FFFD, Balém de U+10FFFF, um byte que nunca aparece e bytes de continuação soltos (Tabela 3-10)
E1 80 E2 F0 91 92 F1 BF 41quatro U+FFFD, depois Auma sequência cortada conta uma vez, quantos bytes dela tenham chegado (Tabela 3-11)

Isso importa para a segurança. A RFC 3629 descreve um analisador que rejeita os bytes 2F 2E 2E 2F (/../) mas aceita a sequência malformada 2F C0 AE 2E 2F e decodifica C0 AE como um ponto, e acrescenta que "this last exploit has actually been used in a widespread virus attacking Web servers in 2001" (esse último ataque foi de fato usado por um vírus que se espalhou por servidores web em 2001). Um decodificador que substitui C0 AE em vez de ler um ponto fecha essa porta; uma verificação que olha os bytes antes de eles serem decodificados não fecha.

Um firewall de aplicações web enfrenta o mesmo problema pelo outro lado: ele precisa ver o que o servidor vai ver. A documentação do BIG-IP ASM da F5 (17.5.0, Working with Violations) lista subviolações de técnicas de evasão que tratam todas de codificação: "Bad unescape" para codificação hexadecimal ilegal como %RR, "Bare byte decoding" para bytes acima de 127, " Unicode codepoints" para os mapeamentos %u próprios do IIS pela página de código Windows-1252, e "Directory traversals" para padrões como ../.

UTF-16: dois bytes, ou quatro

O UTF-16 escreve cada ponto de código até U+FFFF como uma unidade de 16 bits. Acima disso, a RFC 2781 subtrai 0x10000, o que deixa um número de 20 bits, e o divide em dois: os dez bits altos vão para uma unidade que começa em 0xD800, os dez baixos para uma que começa em 0xDC00. Essas duas faixas, D800 a DBFF e DC00 a DFFF, são os surrogates. O Unicode nunca atribui caracteres a elas, então um surrogate em UTF-16 só pode ser metade de um par. O rosto sorridente, U+1F600, vira D83D DE00.

Um surrogate sem o seu par é malformado, e o decodificador o substitui por U+FFFD. Strings em JavaScript são sequências de unidades de 16 bits, então podem conter um surrogate sem par; o TextEncoder escreve um como EF BF BD, os bytes UTF-8 de U+FFFD (medido no Node 22 em 2026-10-03).

Ordem dos bytes: BE, LE e a marca de ordem de bytes

Uma unidade de 16 bits são dois bytes, e as máquinas discordam sobre qual vem primeiro. O UTF-16BE escreve o byte alto primeiro, e o UTF-16LE o byte baixo primeiro. Para texto ASCII essa é a diferença entre 00 48 00 69 e 48 00 69 00 para "Hi", e é assim que o UTF-16 se denuncia num dump: um byte sim, um não, é zero.

O "UTF-16" sem sufixo pode dizer que ordem usa com uma marca de ordem de bytes, U+FEFF logo no início: FE FF em , FF FE em little-endian. O padrão Unicode (D98) acrescenta que "when there is no BOM, and in the absence of a higher-level protocol, the byte order of the UTF-16 encoding scheme is big-endian" (sem BOM e sem um protocolo de nível mais alto, a ordem é big-endian). Os navegadores fazem o contrário: no WHATWG Encoding Standard o rótulo "utf-16" significa UTF-16LE, "to deal with deployed content" (para lidar com o conteúdo já publicado). E quando um navegador encontra uma marca de ordem de bytes no início de um documento, a marca vence qualquer rótulo, porque "has been found to be more accurate in deployed content" (se mostrou mais precisa no conteúdo publicado).

O UTF-8 não tem ordem de bytes a marcar, mas EF BB BF, que é U+FEFF em UTF-8, ainda é usado como assinatura. O padrão Unicode (D95) diz que a presença dela "does not affect conformance" (não afeta a conformidade) e pode ser tomada como "a near-certain indication" (um indício quase certo) de que os dados são UTF-8. Alguns formatos a proíbem: a RFC 8259 diz que implementações de JSON "MUST NOT add a (U+FEFF) to the beginning of a networked-transmitted JSON text" (não podem acrescentar uma marca de ordem de bytes no início de um texto JSON transmitido pela rede), enquanto um analisador pode ignorá-la. A marca é invisível na maioria dos editores, e é assim que ela vai parar onde ninguém queria (veja Caracteres escondidos e sósias).

UTF-32: quatro bytes, sempre

O UTF-32 escreve cada ponto de código como uma unidade de 32 bits, big- ou little-endian, com as marcas 00 00 FE FF e FF FE 00 00. É o mais simples dos três, e o WHATWG Encoding Standard não o define: um navegador não consegue decodificá-lo, e a detecção de marcas de ordem de bytes dele só conhece as marcas do UTF-8 e do UTF-16.

Um roteiro para ler um dump por conta própria

  • Ache os primeiros bytes. No UTF-8 eles dão o comprimento de cada caractere; 80 a BF só continuam um.
  • Conte os zeros. Um byte sim, um não, zero significa UTF-16 (zeros em segundo lugar: little-endian); três bytes em quatro significa UTF-32.
  • Procure uma marca no início. EF BB BF, FE FF, FF FE, 00 00 FE FF ou FF FE 00 00.
  • Deixe a ferramenta fazer. Cole o dump no conversor de charsets: ele lê a saída de hexdump -C, xxd e od, dá nome a cada sequência malformada e mostra o que cada charset faz dos mesmos bytes. Quando os bytes decodificam mas o texto parece errado, o charset era o errado: veja Mojibake.

Fontes

Os leiautes de bits e as propriedades vêm da RFC 3629 e da RFC 2781; as sequências bem formadas, a prática de substituição e os esquemas de codificação, do capítulo 3 do padrão Unicode 18.0.0; os decodificadores, rótulos e o tratamento da marca de ordem de bytes no navegador, do WHATWG Encoding Standard; a regra do JSON, da RFC 8259; as subviolações do ASM, da documentação do BIG-IP 17.5.0 da F5. As decodificações das tabelas foram executadas no conversor de charsets e no TextDecoder do Node 22 em 2026-10-03. Tudo lido em 2026-10-03.