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:
- E6 (230) está por encima del punto de código 128.
- 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:
-
110x xxxx
=> secuencia de 2 bytes -
1110 xxxx
=> secuencia de 3 bytes -
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.
Una referencia excelente para esto son las preguntas frecuentes sobre UTF-8 y Unicode de Markus Kuhn.