traductor teclado significado lista letras español escritura escribir dibujar como chinos chino chinas china caracteres abecedario unicode encoding cjk

unicode - teclado - letras chinas en español



¿Cómo sabe un archivo con caracteres chinos cuántos bytes usar por carácter? (9)

He leído el artículo de Joel "El Absoluto Mínimo de todos los desarrolladores de software Absolutamente, positivamente debemos saber acerca de los conjuntos de caracteres y Unicode (¡sin excusas!") Pero aún no entiendo todos los detalles. Un ejemplo ilustrará mis problemas. Mira este archivo a continuación:

texto alt http://www.yart.com.au/stackoverflow/unicode2.png

Abrí el archivo en un editor binario para examinar de cerca la última de las tres a al lado del primer carácter chino:

texto alt http://www.yart.com.au/stackoverflow/unicode1.png

Según Joel:

En UTF-8, cada punto de código de 0-127 se almacena en un solo byte. Solo los puntos de código 128 y superiores se almacenan utilizando 2, 3, de hecho, hasta 6 bytes.

Así lo dice el editor:

  1. E6 (230) está por encima del punto de código 128.
  2. Por lo tanto, interpretaré los siguientes bytes como 2, 3, de hecho, hasta 6 bytes.

Si es así, ¿qué indica que la interpretación es más de 2 bytes? ¿Cómo es esto indicado por los bytes que siguen a E6?

¿Se almacena mi carácter chino en 2, 3, 4, 5 o 6 bytes?


¿Por qué hay tantas respuestas complicadas?

3 bytes para 1 carácter chino. usando esta función (bajo jQuery):

function get_length(field_selector) { var escapedStr = encodeURI($(field_selector).val()) if (escapedStr.indexOf("%") != -1) { var count = escapedStr.split("%").length - 1 if (count == 0) count++ //perverse case; can''t happen with real UTF-8 var tmp = escapedStr.length - (count * 3) count = count + tmp } else { count = escapedStr.length } return count }



Esencialmente, si comienza con un 0, es un punto de código de 7 bits. Si comienza con 10, es una continuación de un punto de código de múltiples bytes. De lo contrario, el número de 1 le dirá cuántos bytes se codifica este punto de código.

El primer byte indica cuántos bytes codifican el punto de código.

0xxxxxxx 7 bits de código codificado en 1 bytes

110xxxxx 10xxxxxx 10 bits de código codificado en 2 bytes

110xxxxx 10xxxxxx 10xxxxxx etc. 1110xxxx 11110xxx etc.


Eso es todo parte de la codificación UTF8 (que es solo un esquema de codificación para Unicode).

El tamaño puede calcularse examinando el primer byte de la siguiente manera:

  • si comienza con el patrón de bits "10" (0x80-0xbf) , no es el primer byte de una secuencia y debe retroceder hasta que encuentre el inicio, cualquier byte que comience con "0" u "11" (gracias a Jeffrey Hantin por señalarlo en los comentarios).
  • si comienza con el patrón de bits "0" (0x00-0x7f) , es 1 byte.
  • si comienza con el patrón de bits "110" (0xc0-0xdf) , es de 2 bytes.
  • si comienza con el patrón de bits "1110" (0xe0-0xef) , es de 3 bytes.
  • si comienza con el patrón de bits "11110" (0xf0-0xf7) , es de 4 bytes.

Duplicaré la tabla que muestra esto, pero el original está en la página de Wikipedia UTF8 here .

+----------------+----------+----------+----------+----------+ | Unicode | Byte 1 | Byte 2 | Byte 3 | Byte 4 | +----------------+----------+----------+----------+----------+ | U+0000-007F | 0xxxxxxx | | | | | U+0080-07FF | 110yyyxx | 10xxxxxx | | | | U+0800-FFFF | 1110yyyy | 10yyyyxx | 10xxxxxx | | | U+10000-10FFFF | 11110zzz | 10zzyyyy | 10yyyyxx | 10xxxxxx | +----------------+----------+----------+----------+----------+

Los caracteres Unicode en la tabla anterior se construyen a partir de los bits:

000z-zzzz yyyy-yyyy xxxx-xxxx

donde se asume que los bits z e y son cero donde no se dan. Algunos bytes se consideran ilegales como un byte de inicio, ya que son:

  • inútil: una secuencia de 2 bytes que comienza con 0xc0 o 0xc1 en realidad da un punto de código menor que 0x80 que se puede representar mejor con una secuencia de 1 byte.
  • utilizado por RFC3629 para secuencias de 4 bytes por encima de U + 10FFFF, o secuencias de 5 bytes y 6 bytes. Estos son los bytes 0xf5 a 0xfd.
  • Simplemente sin usar: bytes 0xfe y 0xff.

Además, los bytes subsiguientes en una secuencia de múltiples bytes que no comienzan con los bits "10" también son ilegales.

Como ejemplo, considere la secuencia [0xf4,0x8a, 0xaf, 0x8d]. Esta es una secuencia de 4 bytes, ya que el primer byte cae entre 0xf0 y 0xf7.

0xf4 0x8a 0xaf 0x8d = 11110100 10001010 10101111 10001101 zzz zzyyyy yyyyxx xxxxxx = 1 0000 1010 1011 1100 1101 z zzzz yyyy yyyy xxxx xxxx = U+10ABCD

Para su consulta específica con el primer byte 0xe6 (longitud = 3), la secuencia de bytes es:

0xe6 0xbe 0xb3 = 11100110 10111110 10110011 yyyy yyyyxx xxxxxx = 01101111 10110011 yyyyyyyy xxxxxxxx = U+6FB3

Si busca ese código here arriba, verá que es el que tenía en su pregunta:.

Para mostrar cómo funciona la decodificación, volví a mis archivos para encontrar mi código de manejo UTF8. He tenido que cambiarlo un poco para convertirlo en un programa completo y la codificación ha sido eliminada (ya que la pregunta era realmente acerca de la decodificación), así que espero que no haya introducido ningún error al cortar y pegar:

#include <stdio.h> #include <string.h> #define UTF8ERR_TOOSHORT -1 #define UTF8ERR_BADSTART -2 #define UTF8ERR_BADSUBSQ -3 typedef unsigned char uchar; static int getUtf8 (uchar *pBytes, int *pLen) { if (*pLen < 1) return UTF8ERR_TOOSHORT; /* 1-byte sequence */ if (pBytes[0] <= 0x7f) { *pLen = 1; return pBytes[0]; } /* Subsequent byte marker */ if (pBytes[0] <= 0xbf) return UTF8ERR_BADSTART; /* 2-byte sequence */ if ((pBytes[0] == 0xc0) || (pBytes[0] == 0xc1)) return UTF8ERR_BADSTART; if (pBytes[0] <= 0xdf) { if (*pLen < 2) return UTF8ERR_TOOSHORT; if ((pBytes[1] & 0xc0) != 0x80) return UTF8ERR_BADSUBSQ; *pLen = 2; return ((int)(pBytes[0] & 0x1f) << 6) | (pBytes[1] & 0x3f); } /* 3-byte sequence */ if (pBytes[0] <= 0xef) { if (*pLen < 3) return UTF8ERR_TOOSHORT; if ((pBytes[1] & 0xc0) != 0x80) return UTF8ERR_BADSUBSQ; if ((pBytes[2] & 0xc0) != 0x80) return UTF8ERR_BADSUBSQ; *pLen = 3; return ((int)(pBytes[0] & 0x0f) << 12) | ((int)(pBytes[1] & 0x3f) << 6) | (pBytes[2] & 0x3f); } /* 4-byte sequence */ if (pBytes[0] <= 0xf4) { if (*pLen < 4) return UTF8ERR_TOOSHORT; if ((pBytes[1] & 0xc0) != 0x80) return UTF8ERR_BADSUBSQ; if ((pBytes[2] & 0xc0) != 0x80) return UTF8ERR_BADSUBSQ; if ((pBytes[3] & 0xc0) != 0x80) return UTF8ERR_BADSUBSQ; *pLen = 4; return ((int)(pBytes[0] & 0x0f) << 18) | ((int)(pBytes[1] & 0x3f) << 12) | ((int)(pBytes[2] & 0x3f) << 6) | (pBytes[3] & 0x3f); } return UTF8ERR_BADSTART; } static uchar htoc (char *h) { uchar u = 0; while (*h != ''/0'') { if ((*h >= ''0'') && (*h <= ''9'')) u = ((u & 0x0f) << 4) + *h - ''0''; else if ((*h >= ''a'') && (*h <= ''f'')) u = ((u & 0x0f) << 4) + *h + 10 - ''a''; else return 0; h++; } return u; } int main (int argCount, char *argVar[]) { int i; uchar utf8[4]; int len = argCount - 1; if (len != 4) { printf ("Usage: utf8 <hex1> <hex2> <hex3> <hex4>/n"); return 1; } printf ("Input: (%d) %s %s %s %s/n", len, argVar[1], argVar[2], argVar[3], argVar[4]); for (i = 0; i < 4; i++) utf8[i] = htoc (argVar[i+1]); printf (" Becomes: (%d) %02x %02x %02x %02x/n", len, utf8[0], utf8[1], utf8[2], utf8[3]); if ((i = getUtf8 (&(utf8[0]), &len)) < 0) printf ("Error %d/n", i); else printf (" Finally: U+%x, with length of %d/n", i, len); return 0; }

Puedes ejecutarlo con tu secuencia de bytes (necesitarás 4, así que usa 0 para rellenarlos) de la siguiente manera:

> utf8 f4 8a af 8d Input: (4) f4 8a af 8d Becomes: (4) f4 8a af 8d Finally: U+10abcd, with length of 4 > utf8 e6 be b3 0 Input: (4) e6 be b3 0 Becomes: (4) e6 be b3 00 Finally: U+6fb3, with length of 3 > utf8 41 0 0 0 Input: (4) 41 0 0 0 Becomes: (4) 41 00 00 00 Finally: U+41, with length of 1 > utf8 87 0 0 0 Input: (4) 87 0 0 0 Becomes: (4) 87 00 00 00 Error -2 > utf8 f4 8a af ff Input: (4) f4 8a af ff Becomes: (4) f4 8a af ff Error -3 > utf8 c4 80 0 0 Input: (4) c4 80 0 0 Becomes: (4) c4 80 00 00 Finally: U+100, with length of 2


La pista está en esta frase aquí:

En UTF-8, cada punto de código de 0-127 se almacena en un solo byte. Solo los puntos de código 128 y superiores se almacenan utilizando 2, 3, de hecho, hasta 6 bytes.

Cada punto de código hasta 127 tiene el bit superior establecido en cero. Por lo tanto, el editor sabe que si encuentra un byte donde el bit superior es un 1, es el comienzo de un carácter de múltiples bytes.


Los puntos de código hasta 0x7ff se almacenan como 2 bytes; hasta 0xffff como 3 bytes; Todo lo demás como 4 bytes. (Técnicamente, hasta 0x1fffff, pero el punto de código más alto permitido en Unicode es 0x10ffff).

Al decodificar, el primer byte de la secuencia de múltiples bytes se utiliza para determinar el número de bytes utilizados para hacer la secuencia:

  1. 110x xxxx => secuencia de 2 bytes
  2. 1110 xxxx => secuencia de 3 bytes
  3. 1111 0xxx => secuencia de 4 bytes

Todos los bytes subsiguientes en la secuencia deben ajustarse al patrón 10xx xxxx .


Si la codificación es UTF-8, la siguiente tabla muestra cómo un punto de código Unicode (hasta 21 bits) se convierte en codificación UTF-8:

Scalar Value 1st Byte 2nd Byte 3rd Byte 4th Byte 00000000 0xxxxxxx 0xxxxxxx 00000yyy yyxxxxxx 110yyyyy 10xxxxxx zzzzyyyy yyxxxxxx 1110zzzz 10yyyyyy 10xxxxxx 000uuuuu zzzzyyyy yyxxxxxx 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx

Hay una serie de valores no permitidos, en particular, los bytes 0xC1, 0xC2 y 0xF5 - 0xFF nunca pueden aparecer en UTF-8 bien formado. También hay una serie de otras combinaciones verboten. Las irregularidades están en las columnas de 1er byte y 2do byte. Tenga en cuenta que los códigos U + D800 - U + DFFF están reservados para sustitutos de UTF-16 y no pueden aparecer en UTF-8 válido.

Code Points 1st Byte 2nd Byte 3rd Byte 4th Byte U+0000..U+007F 00..7F U+0080..U+07FF C2..DF 80..BF U+0800..U+0FFF E0 A0..BF 80..BF U+1000..U+CFFF E1..EC 80..BF 80..BF U+D000..U+D7FF ED 80..9F 80..BF U+E000..U+FFFF EE..EF 80..BF 80..BF U+10000..U+3FFFF F0 90..BF 80..BF 80..BF U+40000..U+FFFFF F1..F3 80..BF 80..BF 80..BF U+100000..U+10FFFF F4 80..8F 80..BF 80..BF

Estas tablas se levantan de la versión 5.1 estándar de Unicode .

En la pregunta, el material del offset 0x0010 ... 0x008F produce:

0x61 = U+0061 0x61 = U+0061 0x61 = U+0061 0xE6 0xBE 0xB3 = U+6FB3 0xE5 0xA4 0xA7 = U+5927 0xE5 0x88 0xA9 = U+5229 0xE4 0xBA 0x9A = U+4E9A 0xE4 0xB8 0xAD = U+4E2D 0xE6 0x96 0x87 = U+6587 0xE8 0xAE 0xBA = U+8BBA 0xE5 0x9D 0x9B = U+575B 0x2C = U+002C 0xE6 0xBE 0xB3 = U+6FB3 0xE6 0xB4 0xB2 = U+6D32 0xE8 0xAE 0xBA = U+8BBA 0xE5 0x9D 0x9B = U+575B 0x2C = U+002C 0xE6 0xBE 0xB3 = U+6FB3 0xE6 0xB4 0xB2 = U+6D32 0xE6 0x96 0xB0 = U+65B0 0xE9 0x97 0xBB = U+95FB 0x2C = U+002C 0xE6 0xBE 0xB3 = U+6FB3 0xE6 0xB4 0xB2 = U+6D32 0xE4 0xB8 0xAD = U+4E2D 0xE6 0x96 0x87 = U+6587 0xE7 0xBD 0x91 = U+7F51 0xE7 0xAB 0x99 = U+7AD9 0x2C = U+002C 0xE6 0xBE 0xB3 = U+6FB3 0xE5 0xA4 0xA7 = U+5927 0xE5 0x88 0xA9 = U+5229 0xE4 0xBA 0x9A = U+4E9A 0xE6 0x9C 0x80 = U+6700 0xE5 0xA4 0xA7 = U+5927 0xE7 0x9A 0x84 = U+7684 0xE5 0x8D 0x8E = U+534E 0x2D = U+002D 0x29 = U+0029 0xE5 0xA5 0xA5 = U+5965 0xE5 0xB0 0xBA = U+5C3A 0xE7 0xBD 0x91 = U+7F51 0x26 = U+0026 0x6C = U+006C 0x74 = U+0074 0x3B = U+003B


UTF-8 se construye de tal manera que no hay ambigüedad posible acerca de dónde comienza un carácter y cuántos bytes tiene.

Es realmente simple

  • Un byte en el rango de 0x80 a 0xBF nunca es el primer byte de un carácter.
  • Cualquier otro byte es siempre el primer byte de un carácter.

UTF-8 tiene mucha redundancia.

Si quieres saber cuántos bytes de largo es un carácter, hay varias formas de saberlo.

  • El primer byte siempre te dice cuántos bytes de largo es el carácter:
    • Si el primer byte es 0x00 a 0x7F, es un byte.
    • 0xC2 a 0xDF significa que son dos bytes.
    • 0xE0 a 0xEF significa que son tres bytes.
    • 0xF0 a 0xF4 significa que son cuatro bytes.
  • O, simplemente puede contar el número de bytes consecutivos en el rango de 0x80 a 0xBF, porque todos estos bytes pertenecen al mismo carácter que el byte anterior.

Algunos bytes nunca se usan, como 0xC1 a 0xC2 o 0xF5 a 0xFF, por lo que si encuentra estos bytes en cualquier lugar, entonces no está mirando UTF-8.