Por que o UTF-8 desperdiça vários bits em sua codificação

17

De acordo com o artigo da Wikipedia , UTF-8 tem este formato:

Primeiro código Último código Bytes Byte 1 Byte 2 Byte 3 Byte 4
point point Usado
U + 0000 U + 007F 1 0xxxxxxx
U + 0080 U + 07FF 2 110xxxxx 10xxxxxx
U + 0800 U + FFFF 3 1110xxxx 10xxxxxx 10xxxxxx
U + 10000 U + 1FFFFF 4 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
x significa que este bit é usado para selecionar o ponto de código.

Isso desperdiça dois bits em cada byte de continuação e um bit no primeiro byte. Por que o UTF-8 não é codificado da seguinte maneira?

Primeiro código Último código Bytes Byte 1 Byte 2 Byte 3
point point Usado
U + 0000 U + 007F 1 0xxxxxxx
U + 0080 U + 3FFF 2 10xxxxxx xxxxxxxx
U + 0800 U + 1FFFFF 3 110xxxxx xxxxxxxx xxxxxxxx

Ele economizaria um byte quando o ponto de código estiver fora do plano multilíngue básico ou se o ponto de código estiver no intervalo [U + 800, U + 3FFF].

Por que o UTF-8 não é codificado de maneira mais eficiente?

qbt937
fonte
3
cl.cam.ac.uk/~mgk25/ucs/utf-8-history.txt Sua codificação proposta é semelhante à proposta FSS / UTF original. Ken Thompson e Rob Pike queriam a propriedade de auto-sincronização.
Ninjalj 9/03
4
Além disso, sua codificação parece não garantir que os valores do código ASCII não apareçam em nenhuma parte da representação para caracteres não ASCII. O FSS / UTF e o UTF-8 foram projetados para trabalhar com programas herdados (por exemplo: aqueles que usam ASCII NUL e barra (separador de caminho) como separadores).
Ninjalj 9/03

Respostas:

26

Isso é feito para que você possa detectar quando está no meio de uma sequência de vários bytes. Ao analisar dados UTF-8, você sabe que, se vir 10xxxxxx, está no meio de um caractere multibyte e deve fazer backup no fluxo até ver um 0xxxxxxou outro 11xxxxxx. Usando seu esquema, os bytes 2 ou 3 podem acabar facilmente com padrões como um 0xxxxxxxou outro11xxxxxx

Lembre-se também de que a quantidade economizada varia inteiramente de acordo com o tipo de dados que você está codificando. Para a maioria dos textos, mesmo os asiáticos, você raramente vê caracteres de quatro bytes com texto normal. Além disso, as estimativas ingênuas das pessoas sobre a aparência do texto geralmente estão erradas. Eu tenho um texto localizado para UTF-8 que inclui seqüências de caracteres japonesas, chinesas e coreanas, mas é realmente o russo que ocupa mais espaço. (Como nossas seqüências de caracteres asiáticas geralmente têm caracteres romanos intercalados por nomes próprios, pontuação e outros, e porque a palavra média chinesa é de 1 a 3 caracteres, enquanto a palavra média russa é muito, muito mais.)

Gort the Robot
fonte
Mas, com o meu esquema, se você começar em um local que se sabe estar no início de um caractere, poderá dizer quantos bytes existem no caractere e chegar ao início do próximo caractere.
Qbt937
11
Certo. Seu esquema é mais denso em informações, mas não possui um recurso importante que o UTF-8 fornece. Em geral, as pessoas preferem a segurança, e é por isso que o UTF-8 é possível. Além disso, para provar realmente que seu esquema é realmente mais eficiente, você deseja fornecer estatísticas usando texto real. Você pode achar que, na maioria dos textos reais, seu esquema economiza uma quantia muito trivial e, portanto, a economia não vale a pena.
Gort the Robot
3
Uma outra característica importante: se não houver nenhum ponto de código zero incorporado, não haverá zeros incorporados na string.
Deduplicator
Para scripts em tailandês, é necessário permitir 4 bytes por caractere impresso. Eles não apenas chegaram atrasados ​​à festa e também obtiveram um grupo de códigos numerado. Muitas coisas que parecem um único caractere quando impressas são realmente compostas por três caracteres unicode diferentes.
James Anderson
@ qbt937: Usando seu esquema, como uma varredura seria rápida para descobrir se uma string contém outra?
precisa
6

A maneira oficial permite que o decodificador saiba quando está no meio da tupla e sabe pular bytes (ou retroceder) até que o byte comece com 0ou 11; isso evita valores de lixo quando um único byte é corrompido.

catraca arrepiante
fonte
3

Resposta curta, sua proposta não diferencia entre o primeiro byte e os bytes de continuação.

O padrão de bits na extremidade superior do primeiro byte informa com quantos bytes o caractere real é criado. Esses padrões também fornecem algum reconhecimento de erro ao analisar uma string. Se você está lendo o (aparentemente) primeiro byte de um personagem e obtém 10xxxxxx, sabe que está fora de sincronia.

Kitana
fonte
2

O que não foi mencionado é que, se você possui uma sequência correta de pontos de código e um ponteiro que é garantido para apontar para o primeiro byte de um ponto de código, com UTF-8, é possível encontrar facilmente o ponteiro para o primeiro byte do ponto de código anterior (pule todos os bytes que começam com 01xx xxxx). Com sua codificação, é impossível sem examinar potencialmente todos os bytes até o início da string.

Considere as seqüências de (2n + 2) bytes

0xxxxxxx
n times (10xxxxxx, 10xxxxxx)
0xxxxxxx

e

n times (10xxxxxx, 10xxxxxx)
(10xxxxxx, 0xxxxxxx)

Se você tiver um ponteiro para o primeiro byte do primeiro ponto de código após esta sequência, deverá examinar todos os bytes para descobrir se o último ponto de código é 0xxxxxxx ou (10xxxxxx, 0xxxxxxx).

Na verdade, existem esquemas de codificação mais eficientes, nos quais o acesso ao ponto de código anterior pode ser feito em tempo constante e os ponteiros para o meio de um ponto de código podem ser corrigidos. Permita os seguintes códigos:

X where X < 128
YX where 128 ≤ Y < 236, X < 128
ZYY where 236 ≤ Z < 256, 0 ≤ Y < 236. 

Se um dos três bytes anteriores for ≥ 236, será o início de uma sequência de 3 bytes, porque não pode haver dois bytes dentro de qualquer sequência válida de 3 bytes. Caso contrário, se um dos dois bytes anteriores for ≥ 128, será o início de uma sequência de dois bytes. Caso contrário, o byte anterior é um byte único <128.

Procurar uma substring se torna um pouco mais difícil. Você pode excluir zero bytes para que uma string contenha apenas um byte zero se contiver um ponto de código zero.

gnasher729
fonte
O que não foi mencionado ... - não é exatamente isso que se segue diretamente da observação feita na resposta do @ratchet freak.
Piotr Dobrogost 01/03/19