Introducción a UTF-8
(UNICODE Transformation Format)
Windows puede trabajar con cadenas Unicode, SBCS y
DBCS, pero el kernel de Linux trabaja con cadenas UTF-8, donde un
carácter puede ocupar hasta seis bytes. Normalmente uno o dos en idiomas
occidentales y de uno a tres en idiomas asiáticos. UTF-8 es un esquema
de codificación multibyte que puede acomodar todos los caracteres del
UCS (Universal Character Set - Juego de Caracteres Universal), que
contiene caracteres de 31 bits capaces de representar prácticamente
todos los caracteres de idiomas vivos y lenguas muertas, incluídos los
ideogramas como Hiragana, Kiragana, etc. También deja espacio
para más lenguajes, scripts y jeroglíficos.
UTF-8 presenta las siguientes características importantes:
- Codificación de longitud variable para caracteres UCS. UTF-8
puede codificar caracteres UCS (ISO 10646) en hasta 6 bytes.
- Transparencia y univoquidad para caracteres ASCII
Los caracteres ASCII de 7 bits (#0..#127) son codificados directamente
como caracteres ASCII de 7 bits (1 byte por carácter). Todos los
caracteres no ASCII (#128..#255) se representan como valores de 8 bits
no ASCII (#128..#255) para que los caracteres no ASCII no puedan
confundirse con caracteres ASCII, y las herramientas de procesamiento
de texto basadas en ASCII puedan ser usadas con texto UTF-8 siempre y
cuando dejen pasar los caracteres de 8 bits sin interpretación.
- El carácter nulo
El carácter #0 (ASCII NULL) sólo aparece donde se desea un nulo. No
puede ser un byte líder o un byte de relleno por ejemplo.
- Auto-sincronización para procesamiento de alta velocidad
Los patrones de los bits de alto orden desambiguan los límites de los
caracteres, y hace fácil saber si un simple byte es un carácter de un
solo byte (0xxxxxxx), un byte líder (11yyyyyx) o un byte de relleno
(10xxxxxx). Este aspecto es muy importante porque permite que las
funciones que procesan caracteres UTF-8 sean más eficientes que las
funciones para DBCS de Windows. Por ejemplo, una cadena UTF-8 puede
recorrerse hacia atrás y las búsquedas de un carácter multibyte que
comience con un byte principal nunca terminarán en un byte de relleno
en medio de un carácter multibyte no deseado. Y como el byte líder
anuncia la longitud del carácter multibyte, puede contar rápidamente
cuántos bytes saltar al recorrer hacia adelante.
- Amistoso con el procesador
UTF-8 se puede leer y escribir rápidamente con simples operaciones de
enmascaramiento y desplazamiento de bits sin multiplicaciones ni
divisiones (que son operaciones lentas para el procesador).
- Compresión razonable
UTF-8 no es tan compacto como las DBCS de Windows, pero para lenguajes
occidentales es mejor que Unicode, y en el peor de los casos (idiomas
asiáticos) nunca es peor que UCS-4.
- Ordenamiento canónico
UTF-8 conserva el orden de comparación para rutinas simples de
comparación de 8 bits como strcmp (una función estándar del C).
- Caracteres centinela
Los bytes #$FE y #$FF nunca aparecen, así que los puede usar como
centinelas, banderas, señales o para indicar un significado especial
(evitando la posibilidad de confundir un centinela con un carácter
verdadero).
- Detectabilidad
Es fácil de detectar una entrada UTF-8 con alta probabilidad si ve la
firma #$EF#$BB#$BF ('') o si ve caracteres multibyte UTF-8 válidos
dado que es improbable que accidentalmente aparezcan en texto ISO
8859-1 (Latin-1).
Codificación UTF-8
Este es el formato general usado para codificar caracteres UCS en UTF-8:
Bits Bytes Representación
7 1 0xxxxxxx
11 2 110xxxxx 10xxxxxx
16 3 1110xxxx 10xxxxxx 10xxxxxx
21 4 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
26 5 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
31 6 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
Nótese que el número de bits 1 en el byte líder es el número de bytes en
una secuencia multibyte.
El signo de copyright ('©' = #169 = #$A9) en binario es 10101001 y dado
que necesita 8 bits, tenemos que usar dos bytes:
110xxxxx 10xxxxxx
Tenemos que llenar entonces 11 bits (x), así que le agregamos tres ceros
a la izquierda a 10101001:
00010 101001
La representación UTF-8 para el carácter de copyright entonces sería:
11000010 10101001
Podría también representarse con más bytes de lo necesario en secuencias
de cadena "extra-largas" (overlong). Por ejemplo, con cuatro bytes, sería:
11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
000 000000 000010 101001
----------------------------------------
11110000 10000000 10000010 10101001
Las secuencias extra-largas se usan para "camuflar" caracteres y
"engañar" comprobaciones de subcadenas. Por ejemplo, si busca el signo
de copyright exactamente como 11000010 10101001 (la codificación más
corta posible), entonces no lo encontrará.
Longitud de una cadena UTF-8
En Delphi para Linux, las cadenas largas estarán en formato UTF-8,
mientras que las cadenas anchas permanecerán como Unicode de dos bytes.
Para saber el número real de caracteres
almacenados en una cadena UTF-8 podríamos usar una función como la
siguiente:
function UTF8Length(const s: string): integer;
var
i, n: integer;
c: byte;
begin
Result := 0;
n := Length(s);
i := 1;
while i <= n do begin
inc(Result);
c := byte(s[i]);
if (c and $80) = 0 then inc(i)
else if (c and $E0) = $C0 then inc(i, 2)
else if (c and $F0) = $E0 then inc(i, 3)
else if (c and $F8) = $F0 then inc(i, 4)
else if (c and $FC) = $F8 then inc(i, 5)
else if (c and $FE) = $FC then inc(i, 6)
else
raise Exception.Create('No es una cadena UTF-8');
end;
if i > n + 1 then
raise Exception.Create('No es una cadena UTF-8');
end;