O UTF-16 tem largura fixa ou largura variável? Por que o UTF-8 não tem problema de ordem de bytes?


16
  1. O UTF-16 tem largura fixa ou largura variável? Obtive resultados diferentes de diferentes fontes:

    De http://www.tbray.org/ongoing/When/200x/2003/04/26/UTF :

    O UTF-16 armazena caracteres Unicode em pedaços de dezesseis bits.

    De http://en.wikipedia.org/wiki/UTF-16/UCS-2 :

    UTF-16 (formato de transformação Unicode de 16 bits) é uma codificação de caracteres para Unicode capaz de codificar 1.112.064 [1] números (chamados pontos de código) no espaço de código Unicode de 0 a 0x10FFFF. Ele produz um resultado de comprimento variável de uma ou duas unidades de código de 16 bits por ponto de código.

  2. Da primeira fonte

    O UTF-8 também tem a vantagem de que a unidade de codificação é o byte, portanto, não há problemas de ordenação de bytes.

    Por que o UTF-8 não tem problema de ordem de bytes? É de largura variável, e um caractere pode conter mais de um byte, então acho que a ordem dos bytes ainda pode ser um problema?

Obrigado e cumprimentos!


Respostas:


13

(1) O que significa sequência de bytes, um array de char em C? O UTF-16 é uma sequência de bytes ou o que é então? (2) Por que uma sequência de bytes não tem nada a ver com comprimento variável?

Você parece estar entendendo mal quais são os problemas endian. Aqui está um breve resumo.

Um número inteiro de 32 bits ocupa 4 bytes. Agora, sabemos a ordem lógica desses bytes. Se você tiver um número inteiro de 32 bits, poderá obter o byte mais alto com o seguinte código:

uint32_t value = 0x8100FF32;
uint8_t highByte = (uint8_t)((value >> 24) & 0xFF); //Now contains 0x81

Está tudo bem e bem. Onde o problema começa é como várias ferragens armazenam e recuperam números inteiros da memória.

Na ordem Big Endian, uma parte de 4 bytes de memória que você lê como um número inteiro de 32 bits será lida com o primeiro byte sendo o byte alto:

[0][1][2][3]

Na ordem Little Endian, uma parte de 4 bytes de memória que você lê como um número inteiro de 32 bits será lida com o primeiro byte sendo o byte baixo :

[3][2][1][0]

Se você tiver um ponteiro para um ponteiro para um valor de 32 bits, poderá fazer o seguinte:

uint32_t value = 0x8100FF32;
uint32_t *pValue = &value;
uint8_t *pHighByte = (uint8_t*)pValue;
uint8_t highByte = pHighByte[0]; //Now contains... ?

De acordo com C / C ++, o resultado disso é indefinido. Pode ser 0x81. Ou pode ser 0x32. Tecnicamente, ele pode retornar qualquer coisa, mas, para sistemas reais, ele retornará um ou outro.

Se você tiver um ponteiro para um endereço de memória, poderá lê-lo como um valor de 32 bits, um valor de 16 bits ou um valor de 8 bits. Em uma grande máquina endian, o ponteiro aponta para o byte alto; em uma pequena máquina endian, o ponteiro aponta para o byte baixo.

Observe que isso é tudo sobre leitura e gravação de / para a memória. Não tem nada a ver com o código C / C ++ interno. A primeira versão do código, aquela que o C / C ++ não declara indefinida, sempre funcionará para obter o byte alto.

O problema é quando você começa a ler fluxos de bytes. Como de um arquivo.

Valores de 16 bits têm os mesmos problemas que os de 32 bits; eles têm apenas 2 bytes em vez de 4. Portanto, um arquivo pode conter valores de 16 bits armazenados em big endian ou little endian order.

UTF-16 é definido como uma sequência de valores de 16 bits . Efetivamente, é um uint16_t[]. Cada unidade de código individual é um valor de 16 bits. Portanto, para carregar o UTF-16 corretamente, você deve saber qual é a capacidade de endereçamento dos dados.

UTF-8 é definido como uma sequência de valores de 8 bits . É um uint8_t[]. Cada unidade de código individual tem 8 bits de tamanho: um único byte.

Agora, UTF-16 e UTF-8 permitem que várias unidades de código (valores de 16 ou 8 bits) se combinem para formar um ponto de código Unicode (um "caractere", mas esse não é o termo correto; é uma simplificação ) A ordem dessas unidades de código que formam um ponto de código é ditada pelas codificações UTF-16 e UTF-8.

Ao processar UTF-16, você lê um valor de 16 bits, fazendo a conversão endian necessária. Em seguida, você detecta se é um par substituto; se for, você lê outro valor de 16 bits, combina os dois e, a partir disso, obtém o valor do ponto de código Unicode.

Ao processar UTF-8, você lê um valor de 8 bits. Nenhuma conversão endian é possível, pois há apenas um byte. Se o primeiro byte denota uma sequência de vários bytes, você lê algum número de bytes, conforme determinado pela sequência de vários bytes. Cada byte individual é um byte e, portanto, não possui conversão endian. A ordem desses bytes na sequência, assim como a ordem dos pares substitutos em UTF-16, é definida por UTF-8.

Portanto, não pode haver problemas endian com o UTF-8.


10

A resposta de Jeremy Banks está correta, mas não abordou a ordem de bytes.

Quando você usa UTF-16, a maioria dos glifos é armazenada usando uma palavra de dois bytes - mas quando a palavra é armazenada em um arquivo de disco, que ordem você usa para armazenar os bytes constituintes?

Como exemplo, o glifo CJK (chinês) da palavra "água" possui uma codificação UTF-16 em hexadecimal de 6C34. Quando você escreve isso como dois bytes no disco, você o escreve como "big endian" (os dois bytes são 6C 34)? Ou você escreve como "little-endian" (os dois bytes são 34 6C)?

Com o UTF-16, ambas as ordens são legítimas e você geralmente indica qual delas o arquivo possui, tornando a primeira palavra no arquivo uma Byte Order Mark (BOM), que para codificação big endian é FE FF e para little endian codificação é FF FE.

UTF-32 tem o mesmo problema e a mesma solução.

O UTF-8 não tem esse problema, porque é de tamanho variável, e você efetivamente escreve a sequência de bytes de um glifo como se fosse little endian. Por exemplo, a letra "P" é sempre codificada usando um byte - 80 - e o caractere de substituição é sempre codificado usando os dois bytes FF FD nessa ordem.

Alguns programas colocam um indicador de três bytes (EF BB BF) no início de um arquivo UTF-8, e isso ajuda a distinguir UTF-8 de codificações semelhantes como ASCII, mas isso não é muito comum, exceto no MS Windows.


Obrigado! (1) a letra "P" é apenas um byte em UTF-8. Por que o caractere de substituição é adicionado ao seu código? (2) No UTF-8, existem outros caracteres que possuem mais de um byte no UTF-8. Por que a ordem de bytes entre bytes para cada caractere não é um problema?
StackExchange for All

@ Tim: (1) Você não adiciona o caractere de substituição ao código de P. Se você vir 80 FF FD, são dois caracteres - um caractere P e um caractere de substituição.
Bob Murphy

(2) Você sempre escreve e lê os dois bytes para o "caractere de substituição" como FF FD, nessa ordem. Só haveria um problema de ordem de bytes se você também pudesse escrever o "caractere de substituição" como FD FF - mas não pode; essa sequência de dois bytes seria algo diferente de um "caractere de substituição".
23711 Bob Murphy

1
@ Tim: Você pode querer trabalhar com en.wikipedia.org/wiki/UTF-8 . É realmente muito bom, e se você puder entender tudo isso e as outras páginas da Wikipedia relacionadas ao Unicode, acho que você descobrirá que não tem mais perguntas.
Bob Murphy

4
O motivo pelo qual o UTF-8 não tem problemas com a ordem dos bytes é que a codificação é definida como uma sequência de bytes e que não há variações com endianness diferente. Não tem nada a ver com comprimento variável.
starblue
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.