¿UTF-16 tiene ancho fijo o ancho variable? ¿Por qué UTF-8 no tiene un problema de orden de bytes?

16
  1. ¿UTF-16 tiene ancho fijo o ancho variable? Obtuve diferentes resultados de diferentes fuentes:

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

    UTF-16 almacena caracteres Unicode en fragmentos de dieciséis bits.

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

    UTF-16 (formato de transformación Unicode de 16 bits) es una codificación de caracteres para Unicode capaz de codificar 1,112,064 [1] números (llamados puntos de código) en el espacio de código Unicode de 0 a 0x10FFFF. Produce un resultado de longitud variable de una o dos unidades de código de 16 bits por punto de código.

  2. De la primera fuente

    UTF-8 también tiene la ventaja de que la unidad de codificación es el byte, por lo que no hay problemas de orden de bytes.

    ¿Por qué UTF-8 no tiene un problema de orden de bytes? Es de ancho variable, y un carácter puede contener más de un byte, ¿así que creo que el orden de bytes puede ser un problema?

¡Gracias y saludos!

StackExchange para todos
fuente
Este gran artículo El Absoluto mínimo Cada desarrollador de software absolutamente, positivamente debe saber sobre Unicode y juegos de caracteres (Sin excusas!) Le ayudará a responder todas sus preguntas acerca de Unicode y UTF ..
Sorceror

Respuestas:

13

(1) ¿Qué significa la secuencia de bytes, un archivo de caracteres en C? ¿Es UTF-16 una secuencia de bytes, o qué es entonces? (2) ¿Por qué una secuencia de bytes no tiene nada que ver con la longitud variable?

Parece que no entiendes cuáles son los problemas endianos. Aquí hay un breve resumen.

Un entero de 32 bits ocupa 4 bytes. Ahora, sabemos el orden lógico de estos bytes. Si tiene un número entero de 32 bits, puede obtener el byte alto de esto con el siguiente código:

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

Eso está muy bien. Donde comienza el problema es cómo varios almacenes de hardware y recupera enteros de la memoria.

En orden Big Endian, se leerá un fragmento de memoria de 4 bytes que se lee como un entero de 32 bits, siendo el primer byte el byte alto:

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

En el orden Little Endian, se leerá un fragmento de memoria de 4 bytes que se lee como un entero de 32 bits, siendo el primer byte el byte bajo :

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

Si tiene un puntero a un puntero a un valor de 32 bits, puede hacer esto:

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

Según C / C ++, el resultado de esto no está definido. Podría ser 0x81. O podría ser 0x32. Técnicamente, podría devolver cualquier cosa, pero para sistemas reales, devolverá uno u otro.

Si tiene un puntero a una dirección de memoria, puede leer esa dirección como un valor de 32 bits, un valor de 16 bits o un valor de 8 bits. En una máquina endian grande, el puntero apunta al byte alto; en una pequeña máquina endian, el puntero apunta al byte bajo.

Tenga en cuenta que esto se trata de leer y escribir en / desde la memoria. No tiene nada que ver con el código interno C / C ++. La primera versión del código, la que C / C ++ no declara como indefinida, siempre funcionará para obtener el byte alto.

El problema es cuando comienzas a leer flujos de bytes. Tal como de un archivo.

Los valores de 16 bits tienen los mismos problemas que los de 32 bits; solo tienen 2 bytes en lugar de 4. Por lo tanto, un archivo podría contener valores de 16 bits almacenados en orden big endian o little endian.

UTF-16 se define como una secuencia de valores de 16 bits . Efectivamente, es un uint16_t[]. Cada unidad de código individual es un valor de 16 bits. Por lo tanto, para cargar correctamente UTF-16, debe saber cuál es la capacidad de los datos.

UTF-8 se define como una secuencia de valores de 8 bits . Es un uint8_t[]. Cada unidad de código individual tiene un tamaño de 8 bits: un solo byte.

Ahora, tanto UTF-16 como UTF-8 permiten que múltiples unidades de código (valores de 16 bits u 8 bits) se combinen para formar un punto de código Unicode (un "carácter", pero ese no es el término correcto; es una simplificación ) El orden de estas unidades de código que forman un punto de código está dictado por las codificaciones UTF-16 y UTF-8.

Al procesar UTF-16, lee un valor de 16 bits, haciendo cualquier conversión endian que sea necesaria. Luego, detecta si es un par sustituto; si es así, entonces lee otro valor de 16 bits, combina los dos, y de eso, obtiene el valor de punto de código Unicode.

Al procesar UTF-8, lee un valor de 8 bits. No es posible la conversión endiana, ya que solo hay un byte. Si el primer byte denota una secuencia de varios bytes, entonces lee algún número de bytes, según lo dictado por la secuencia de varios bytes. Cada byte individual es un byte y, por lo tanto, no tiene conversión endiana. El orden de estos bytes en la secuencia, al igual que el orden de los pares sustitutos en UTF-16, está definido por UTF-8.

Por lo tanto, no puede haber problemas endian con UTF-8.

Nicol Bolas
fuente
10

La respuesta de Jeremy Banks es correcta en lo que va, pero no abordó el pedido de bytes.

Cuando usa UTF-16, la mayoría de los glifos se almacenan usando una palabra de dos bytes, pero cuando la palabra se almacena en un archivo de disco, ¿qué orden usa para almacenar los bytes constituyentes?

Como ejemplo, el glifo CJK (chino) para la palabra "agua" tiene una codificación UTF-16 en hexadecimal de 6C34. Cuando escribe eso como dos bytes en el disco, ¿lo escribe como "big-endian" (los dos bytes son 6C 34)? ¿O lo escribes como "little-endian (los dos bytes son 34 6C)?

Con UTF-16, ambos ordenamientos son legítimos, y generalmente indica cuál tiene el archivo haciendo que la primera palabra en el archivo sea una Marca de orden de bytes (BOM), que para la codificación big-endian es FE FF, y para little-endian la codificación es FF FE.

UTF-32 tiene el mismo problema y la misma solución.

UTF-8 no tiene este problema, porque es de longitud variable, y efectivamente escribe la secuencia de bytes de un glifo como si fuera little-endian. Por ejemplo, la letra "P" siempre se codifica usando un byte - 80 - y el carácter de reemplazo siempre se codifica usando los dos bytes FF FD en ese orden.

Algunos programas colocan un indicador de tres bytes (EF BB BF) al comienzo de un archivo UTF-8, y eso ayuda a distinguir UTF-8 de codificaciones similares como ASCII, pero eso no es muy común, excepto en MS Windows.

Bob Murphy
fuente
¡Gracias! (1) la letra "P" es solo un byte en UTF-8. ¿Por qué se agrega el carácter de reemplazo a su código? (2) En UTF-8, hay otros caracteres que tienen más de un byte en UTF-8. ¿Por qué el orden de bytes entre bytes para cada uno de esos caracteres no es un problema?
StackExchange for All
@Tim: (1) No agrega el carácter de reemplazo al código para P. Si ve 80 FF FD, son dos caracteres: un carácter P y un carácter de reemplazo.
Bob Murphy
(2) Siempre escribe y lee los dos bytes para el "carácter de reemplazo" como FF FD, en ese orden. Solo habría un problema de orden de bytes si también pudiera escribir el "carácter de reemplazo" como FD FF, pero no puede; esa secuencia de dos bytes sería algo más que un "carácter de reemplazo".
Bob Murphy
1
@Tim: es posible que desee trabajar en en.wikipedia.org/wiki/UTF-8 . Es realmente bastante bueno, y si puedes entenderlo todo y las otras páginas de Wikipedia relacionadas con Unicode, creo que descubrirás que no tienes más preguntas al respecto.
Bob Murphy
44
La razón por la que UTF-8 no tiene problemas con el orden de los bytes es que la codificación se define como una secuencia de bytes y que no hay variaciones con diferente endianness. No tiene nada que ver con longitud variable.
starblue