Como funciona a “codificação de largura variável” UTF-8?

110

O padrão Unicode tem pontos de código suficientes para que você precise de 4 bytes para armazenar todos eles. É isso que a codificação UTF-32 faz. Ainda assim, a codificação UTF-8 de alguma forma os espreme em espaços muito menores usando algo chamado "codificação de largura variável".

Na verdade, ele consegue representar os primeiros 127 caracteres de US-ASCII em apenas um byte que se parece exatamente com ASCII real, então você pode interpretar muito texto ASCII como se fosse UTF-8 sem fazer nada a ele. Belo truque. Então, como isso funciona?

Vou fazer e responder minha própria pergunta aqui porque acabei de ler um pouco para descobrir e pensei que poderia economizar algum tempo para outra pessoa. Além disso, talvez alguém possa me corrigir se eu errar em alguma parte.

dsimard
fonte
8
O Unicode reto não requer 32 bits para codificar todos os seus pontos de código. Certa vez, eles reivindicaram muitos pontos de código possíveis, mas depois que o UTF-8 decolou, eles se limitaram intencionalmente a 21 bits, de modo que o UTF-8 nunca excederá 4 bytes por caractere. Unicode atualmente requer apenas 17 bits para conter todos os pontos de código possíveis. Sem essa limitação, o UTF-8 poderia ter chegado a 6 bytes por caractere.
Warren Young,
@ Warren: geralmente preciso, mas Unicode é um código de 21 bits (U + 0000 a U + 10FFFF).
Jonathan Leffler,
2
@ Warren: UTF-8 com limitação de 4 bytes poderia ter suportado até U + 1FFFFF. A restrição a U + 10FFFF foi feita em consideração ao UTF-16.
dan04
@ dan04 Temos alguma explicação fácil de como ele é restrito a U + 10FFFF por UTF-16? Seria bom saber mais sobre isso.
A-letubby
@ A-letubby: Porque os códigos UTF-16 “substitutos” são alocados de forma que haja 1024 substitutos principais e 1024 substitutos substitutos (e eles só podem ser usados ​​em pares), para fazer 2 ^ 20 (cerca de um milhão) de caracteres adicionais disponível além do BMP. Adicionado aos 2 ^ 16 caracteres disponíveis no BMP, isso torna 0x110000 caracteres possíveis.
dan04

Respostas:

129

Cada byte começa com alguns bits que indicam se é um ponto de código de byte único, um ponto de código de vários bytes ou uma continuação de um ponto de código de vários bytes. Como isso:

0xxx xxxx    A single-byte US-ASCII code (from the first 127 characters)

Cada um dos pontos de código multibyte começa com alguns bits que essencialmente dizem "ei, você também precisa ler o próximo byte (ou dois ou três) para descobrir o que eu sou." Eles são:

110x xxxx    One more byte follows
1110 xxxx    Two more bytes follow
1111 0xxx    Three more bytes follow

Por fim, todos os bytes que seguem esses códigos iniciais se parecem com isto:

10xx xxxx    A continuation of one of the multi-byte characters

Como você pode dizer para que tipo de byte está olhando desde os primeiros bits, mesmo que algo seja mutilado em algum lugar, você não perde a sequência inteira.

dsimard
fonte
14
A história é mais do que isso - porque a codificação deve ser a mais curta possível para o personagem, o que acaba significando que os bytes 0xC0 e 0xC1 não podem aparecer em UTF-8, por exemplo; e, de fato, 0xF5..0xFF também não. Veja o UTF-8 FAQ em unicode.org/faq/utf_bom.html , ou unicode.org/versions/Unicode5.2.0/ch03.pdf
Jonathan Leffler
2
Por que não poderia usar apenas um caractere para dizer next char is continuation? Se tivéssemos um caractere de 3 bytes, seria como:, 1xxxxxxx 1xxxxxxx 0xxxxxxxportanto, menos espaço seria desperdiçado.
9
@Soaku, torna o UTF-8 um código chamado de "auto-sincronização". Isso significa que se, devido a erros, partes da sequência estiverem faltando, é possível detectar isso e descartar o que estiver truncado. Se você ler um byte que começa com 10xx e não houver um byte "inicial" anterior, você pode descartá-lo, pois não faz sentido. Se você tivesse um sistema como o descrito, e um dos primeiros bytes fosse perdido, você poderia acabar com um caractere válido diferente, sem indicação de qualquer tipo de erro. Também tornará mais fácil localizar o próximo caractere válido, bem como corrigir os bytes de "continuação" ausentes.
htmlcoderexe
9

RFC3629 - UTF-8, um formato de transformação da ISO 10646 é a autoridade final aqui e tem todas as explicações.

Em suma, vários bits em cada byte da sequência de 1 a 4 bytes codificada em UTF-8 que representa um único caractere são usados ​​para indicar se é um byte à direita, um byte à esquerda e, em caso afirmativo, quantos bytes seguem. Os bits restantes contêm a carga útil.

Azheglov
fonte
1
Ummmm, bobagem, pensei que o padrão Unicode era a autoridade final em UTF-8
John Machin
6
O padrão Unicode define o próprio Unicode. Ele não define vários métodos, atuais e futuros, que podem ser usados ​​para codificar textos Unicode para uma variedade de propósitos (como armazenamento e transporte). UTF-8 é um desses métodos e a referência acima é para o documento que o define.
azheglov 01 de
1
RFC3629, página 3, seção 3. diz "UTF-8 é definido pelo padrão Unicode".
John Machin de
A busca de links em unicode.org me levou à seção 3.9 do Padrão Unicode e, especificamente, à definição D92 (e também tangencialmente D86). Não tenho ideia de até que ponto este link será útil quando novas versões forem lançadas, mas imagino que eles desejam manter os identificadores de seção e definição estáveis ​​entre as versões.
tripleee
4

UTF-8 era outro sistema para armazenar sua string de pontos de código Unicode, aqueles números mágicos U +, na memória usando bytes de 8 bits. No UTF-8, cada ponto de código de 0 a 127 é armazenado em um único byte. Apenas os pontos de código 128 e acima são armazenados usando 2, 3, de fato, até 6 bytes.

Trecho do Mínimo Absoluto Todo Desenvolvedor de Software Absolutamente, Positivamente Deve Saber Sobre Unicode e Conjuntos de Caracteres (Sem Desculpas!)

Andrew
fonte
Esse é um bom artigo, mas parece que Joel está errado quanto ao comprimento máximo da sequência; a página da Wikipedia mostra 1..4 bytes por caractere, apenas.
relaxe em
4
Como eu disse acima, quando o UTF-8 foi criado pela primeira vez, o Unicode reivindicava até 32 bits para pontos de código, não porque eles realmente precisassem, apenas porque 32 bits é um valor conveniente e eles já haviam ultrapassado o limite anterior de caracteres de 16 bits. Depois que o UTF-8 se tornou popular, eles optaram por limitar para sempre o número máximo de pontos de código a 2 ^ 21, sendo esse o maior valor que você pode codificar com 4 bytes do esquema UTF-8. Ainda existem menos de 2 ^ 17 caracteres em Unicode, portanto, podemos mais do que quadruplicar o número de caracteres em Unicode com este novo esquema.
Warren Young,
Ok, mas não a explicação pedida pelo OP.
Nishant
2
Isso não está respondendo à pergunta.
Koray Tugay