SDK Multiplataforma en C logo

SDK Multiplataforma en C

Unicode

❮ Anterior
Siguiente ❯

Funciones

uint32_tunicode_convers (...)
uint32_tunicode_convers_n (...)
uint32_tunicode_convers_nbytes (...)
uint32_tunicode_nbytes (...)
uint32_tunicode_nchars (...)
uint32_tunicode_to_u32 (...)
uint32_tunicode_to_u32b (...)
uint32_tunicode_to_char (...)
bool_tunicode_valid_str (...)
bool_tunicode_valid_str_n (...)
bool_tunicode_valid (...)
const char_t*unicode_next (...)
const char_t*unicode_back (...)
bool_tunicode_isascii (...)
bool_tunicode_isalnum (...)
bool_tunicode_isalpha (...)
bool_tunicode_iscntrl (...)
bool_tunicode_isdigit (...)
bool_tunicode_isgraph (...)
bool_tunicode_isprint (...)
bool_tunicode_ispunct (...)
bool_tunicode_isspace (...)
bool_tunicode_isxdigit (...)
bool_tunicode_islower (...)
bool_tunicode_isupper (...)
uint32_tunicode_tolower (...)
uint32_tunicode_toupper (...)

Unicode es un estándar de la industria informática, en esencia una tabla, que asigna un número único a cada símbolo de cada idioma en el mundo (Figura 1). Estos valores generalmente se denominan codepoints y se representan escribiendo U+ seguido de su número en hexadecimal.

  • Utiliza unicode_convers para convertir una cadena de una codificación a otra.
  • Utiliza unicode_to_u32 para obtener el primer codepoint de una cadena.
  • Tabla que muestra varios símbolos Unicode, junto con su codepoint.
    Figura 1: Varios codepoints Unicode.

Hablando de su estructura, tiene 17 planos de 65536 codepoints cada uno (256 bloques de 256 elementos) (Figura 2). Esto le da a Unicode un límite teórico de 1114112 caracteres, de los cuales 136755 ya se han ocupado (versión 10.0 de junio de 2017). Para aplicaciones del mundo real, el más importante es el Plano 0 denominado Plano Multilingüe Básico (BMP), que incluye los símbolos de todas las idiomas modernos del mundo. Los planos superiores contienen carácteres históricos y símbolos adicionales poco convencionales.

Representación simplificada de un plano de Unicode.
Figura 2: Unicode tiene 17 planos de 256x256 codepoints cada uno.

Las primeros computadores utilizaban ASCII American Standard Code for Information Interchange, un código de 7 bits que define todos los caracteres del idioma Inglés: 26 letras minúsculas (sin signos diacríticos), 26 letras mayúsculas, 10 dígitos, 32 símbolos de puntuación, 33 códigos de control y un espacio en blanco, para un total de 128 posiciones. Tomando el bit adicional dentro de un byte, tendremos espacio para otros 128 símbolos, pero aún insuficiente para todos. Esto da como resultado numerosas páginas de códigos ASCII extendidos, lo que es un gran problema para compartir textos, ya que el mismo código numérico puede representar diferentes símbolos según la página ASCII utilizada (Figura 3).

Comparación de las tablas ASCII 1252 y 1253 en Windows.
Figura 3: En cada página del ASCII Extendido, los 128 códigos superiores representan diferentes caracteres.

Ya a principios de los 90, con la llegada de Internet, este problema se agravó, ya que el intercambio de información entre máquinas de diferente naturaleza y país se convirtió en algo cotidiano. El Consorcio de Unicode (Figura 4) se contituyó en California en enero de 1991 y, en octubre del mismo año, se publicó el primer volumen del estándar Unicode.

Logotipos de las principales empresas del sector informático.
Figura 4: Miembros de pleno derecho del Consorcio Unicode.

1. Codificaciones UTF

Cada codepoint necesita 21 bits para ser representado (5 para el plano y 16 para el desplazamiento). Esto casa muy mal con los tipos básicos en ordenadores (8, 16 o 32 bits). Por este motivo, se han definido tres codificaciones Unicode Translation Format - UTF función del tipo de dato que se utilice en la representación (Figura 5).

Array de bits que representa un valor Unicode.
Figura 5: Codificaciones para almacenar codepoints de 21-bit mediante elementos de 8, 16, o 32.

1.1. UTF-32

Sin ningún problema, utilizando 32 bits podemos almacenar cualquier codepoint. También podemos acceder aleatoriamente mediante un índice a los elementos de un array, de la misma forma que las cadenas ASCII clásicas de C (char). Las malas noticias son los requisitos de memoria. Una cadena UTF32 necesita cuatro veces más espacio que una ASCII.

1
2
3
4
5
6
7
8
9
const char32_t code1[] = U"Hello";
const char32_t code2[] = U"áéíóú";
uint32_t s1 = sizeof(code1); /* s1 == 24 */
uint32_t s2 = sizeof(code2); /* s2 == 24 */
for (i = 0; i < 5; ++i)
{    /* Accessing by index */
     if (code1[i] == 'H')
         return i;
}

1.2. UTF-16

UTF16 reduce a la mitad el espacio requerido por UTF32. Es posible almacenar un codepoint por elemento siempre que no abandonemos el plano 0 (BMP). Para planos superiores, serán necesarios dos elementos UTF16 (32bits). Este mecanismo, que encapsula los planos superiores dentro del BMP, se conoce como pares subrogados.

1
2
3
4
5
6
7
8
9
const char16_t code1[] = u"Hello";
const char16_t code2[] = u"áéíóú";
uint32_t s1 = sizeof(code1); /* s1 == 12 */
uint32_t s2 = sizeof(code2); /* s2 == 12 */
for (i = 0; i < 5; ++i)
{    /* DANGER! Only BMP */
     if (code1[i] == 'H')
         return i;
}
Para iterar sobre una cadena UTF16 que contenga caracteres de cualquier plano debe utilizarse unicode_next.

1.3. UTF-8

UTF8 es un código de longitud variable donde cada codepoint utiliza 1, 2, 3 o 4 bytes.

  • 1 byte (0-7F): los 128 símbolos del ASCII original. Esto supone una gran ventaja, ya que las cadenas US-ASCII son cadenas UTF8 válidas, sin necesidad de conversión.
  • 2 bytes (80-7FF): Caracteres diacríticos y de lenguaje romance, griego, cirílico, coptos, armenio, hebreo, árabe, siríaco y thaana, entre otros. Un total de 1920 codepoints.
  • 3 bytes (800-FFFF): Resto del plano 0 (BMP).
  • 4 bytes (10000-10FFFF): Planos superiores (1-16).
  • Tabla con los rangos de UTF8.
    Figura 6: Cada carácter en UTF8 emplea 1, 2, 3 o 4 bytes.

Más del 90% de los sitios web utilizan UTF8 (agosto de 2018), porque es el más óptimo en términos de memoria y velocidad de transmisión en red. Como desventaja, tiene asociado un pequeño coste computacional para codificar/decodificar, ya que es necesario realizar operaciones de nivel de bit para obtener los codepoints. Tampoco es posible acceder aleatoriamente mediante índice a un carácter concreto, tenemos que procesar la cadena completa.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
const char_t code1[] = "Hello";
const char_t code2[] = "áéíóú";
const char_t *iter = code1;
uint32_t s1 = sizeof(code1); /* s1 == 6 */
uint32_t s2 = sizeof(code2); /* s2 == 11 */
for (i = 0; i < 5; ++i)
{
    if (unicode_to_u32(iter, ekUTF8) == 'H')
         return i;
    iter = unicode_next(iter, ekUTF8);
}

1.4. Uso de UTF-8

UTF8 es la codificación requerida por todas las funciones del SDK NAppGUI. Las razones por las que hemos elegido UTF-8 sobre otras codificaciones han sido:

  • Es la evolución natural del US-ASCII.
  • Las aplicaciones serán directamente compatibles con la gran mayoría de servicios de Internet (JSON/XML).
  • En entornos multi-lenguaje los textos ocuparán menos espacio. Estadísticamente, los 128 caracteres ASCII son los más utilizados en promedio y solo necesitan un byte en UTF8.
  • Como desventaja, en aplicaciones dirigidas exclusivamente al mercado asiático (China, Japón, Corea - CJK), UTF8 es menos eficiente que UTF16.

Dentro de aplicaciones NAppGUI pueden cohexistir diferentes representaciones (char16_t, char32_t, wchar_t). No obstante, recomendamos encarecidamente el uso de UTF8 en favor de la portabilidad y para evitar las constantes conversiones dentro del API. Para convertir cualquier cadena a UTF8 se utiliza la función unicode_convers.

1
2
3
wchar_t text[] = L"My label text.";
char_t ctext[128];
unicode_convers((const char_t*)text, ctext, ekUTF16, ekUTF8, 128);
NAppGUI no ofrece soporte para convertir páginas del ASCII Extendido a Unicode.

El objeto Stream proporciona conversiones automáticas de UTF al leer o escribir en canales de E/S mediante los métodos stm_set_write_utf y stm_set_read_utf. También es posible trabajar con el tipo String (cadenas dinámicas), que incorpora multitud de funciones optimizadas para el tratamiento UTF8. Podemos incluir cadenas de texto constantes directamente en el código fuente (Figura 7), aunque lo habitual será escribirlas en archivos de recursos (Recursos). Evidentemente, deberemos guardar tanto los archivos de código fuente como los de recursos en UTF8. Todos los entornos de desarrollo actuales soportan la opción:

  • De forma predeterminada, Visual Studio guarda los archivos fuente en formato ASCII (Windows 1252). Para cambiar a UTF8, ir a File->Save As->Save with encoding-> Unicode (UTF8 Without Signature) - Codepage 65001. No hay forma de establecer esta configuración para todo el proyecto :-(.
  • En Xcode es posible establecer una configuración global. Preferences->Text editing->Default Text Encoding->Unicode (UTF-8).
  • En Eclipse también permite una configuración global. Window->Preferences->General->Workspace->Text file encoding.
  • Captura de un editor de texto con cadenas en UTF8.
    Figura 7: Constantes UTF8 en un archivo fuente en C.

unicode_convers ()

Convierte una cadena Unicode de una codificación a otra.

uint32_t
unicode_convers(const char_t *from_str,
                char_t *to_str,
                const unicode_t from,
                const unicode_t to,
                const uint32_t osize);
1
2
3
const char32_t str[] = U"Hello World";
char_t utf8_str[256];
unicode_convers((const char_t*)str, utf8_str, ekUTF32, ekUTF8, 256);
from_str

Cadena de origen (terminada en carácter nulo '\0').

to_str

Buffer de destino.

from

Codificación de cadena origen.

to

Codificación requerida en to_str.

osize

Tamaño del búfer de salida. Número máximo de bytes que se escribirán en to_str, incluido el carácter nulo ('\0'). Si la cadena original no se puede copiar en su totalidad, se cortará y se agregará el carácter nulo.

Retorna

Número de bytes escritos en to_str (incluido el carácter nulo).


unicode_convers_n ()

Igual que unicode_convers, pero indicando un tamaño máximo para la cadena de entrada.

uint32_t
unicode_convers_n(const char_t *from_str,
                  char_t *to_str,
                  const unicode_t from,
                  const unicode_t to,
                  const uint32_t isize,
                  const uint32_t osize);
from_str

Cadena de origen.

to_str

Buffer de destino.

from

Codificación de cadena origen.

to

Codificación requerida en to_str.

isize

Tamaño de la cadena en entrada (en bytes).

osize

Tamaño del búfer de salida.

Retorna

Número de bytes escritos en to_str.


unicode_convers_nbytes ()

Calcula el número de bytes necesarios para convertir una cadena Unicode de una codificación a otra. Será útil calcular el espacio necesario en reserva dinámica de memoria.

uint32_t
unicode_convers_nbytes(const char_t *str,
                       const unicode_t from,
                       const unicode_t to);
1
2
3
const char32_t str[] = U"Hello World";
uint32_t size = unicode_convers_nbytes((char_t*)str, ekUTF32, ekUTF8);
/ * size == 12 * /
str

Cadena de origen (terminada en nulo).

from

Codificación de str.

to

Codificación requerida.

Retorna

Número de bytes necesarios (incluido el carácter nulo).


unicode_nbytes ()

Obtiene el tamaño (en bytes) de una cadena Unicode.

uint32_t
unicode_nbytes(const char_t *str,
               const unicode_t format);
str

Cadena Unicode (terminada en '\0').

format

Codificación de str.

Retorna

El tamaño en bytes ('\0' incluido).


unicode_nchars ()

Calcula la longitud (en caracteres) de una cadena Unicode.

uint32_t
unicode_nchars(const char_t *str,
               const unicode_t format);
str

Cadena Unicode (terminada en '\0').

format

Codificación de str.

Retorna

El número de caracteres ('\0' no incluido).

Observaciones

En cadenas ASCII, el número de bytes es igual al número de caracteres. En Unicode depende de la codificación y la cadena.


unicode_to_u32 ()

Obtiene el valor del primer codepoint de la cadena Unicode.

uint32_t
unicode_to_u32(const char_t *str,
               const unicode_t format);
1
2
3
char_t str[] = "áéíóúÄÑ£";
uint32_t cp = unicode_to_u32(str, ekUTF8);
/* cp == 'á' == 225 == U+E1 */
str

Cadena Unicode (terminada en '\0').

format

Codificación de str.

Retorna

El código del primer carácter de str.


unicode_to_u32b ()

Igual que unicode_to_u32 pero con con campo adicional para almacenar la cantidad de bytes que ocupa el codepoint.

uint32_t
unicode_to_u32b(const char_t *str,
                const unicode_t format,
                uint32_t *bytes);
str

Cadena Unicode (terminada en '\0').

format

Codificación de str.

bytes

Guarda el número de bytes necesarios para representar el codepoint mediante format.

Retorna

El código del primer carácter de str.


unicode_to_char ()

Escribe el codepoint al comienzo de str, utilizando la codificación format.

uint32_t
unicode_to_char(const uint32_t codepoint,
                char_t *str,
                const unicode_t format);
1
2
3
4
5
char_t str[64] = \"\";
uint32_t n = unicode_to_char(0xE1, str, ekUTF8);
unicode_to_char(0, str + n, ekUTF8);
/* str == "á" */
/* n = 2 */
codepoint

Código del carácter.

str

Cadena de destino.

format

Codificación para codepoint.

Retorna

El número de bytes escritos (1, 2, 3 or 4).

Observaciones

Para escribir varios codepoints, combinar unicode_to_char con unicode_next.


unicode_valid_str ()

Comprueba si una cadena es Unicode.

bool_t
unicode_valid_str(const char_t *str,
                  const unicode_t format);
str

Cadena a comprobar (terminada en '\0').

format

Codificación Unicode esperada.

Retorna

TRUE si es válida.


unicode_valid_str_n ()

Igual que unicode_valid_str, pero indicando un tamaño máximo para la cadena de entrada.

bool_t
unicode_valid_str_n(const char_t *str,
                    const uint32_t size,
                    const unicode_t format);
str

Cadena a comprobar.

size

Máximo tamaño de la cadena (en bytes).

format

Codificación Unicode esperada.

Retorna

TRUE si es válida.


unicode_valid ()

Comprueba si un codepoint es válido.

bool_t
unicode_valid(const uint32_t codepoint);
codepoint

El código Unicode del carácter.

Retorna

TRUE si el parámetro es un codepoint válido. De lo contrario, FALSE.


unicode_next ()

Avanza al siguiente carácter de una cadena Unicode. En general no es posible el acceso aleatorio como hacemos en ANSI-C (str[i++]). Debemos iterar una cadena desde el principio. Ver Codificaciones UTF.

const char_t*
unicode_next(const char_t *str,
             const unicode_t format);
1
2
3
4
5
6
7
8
9
char_t str[] = "áéíóúÄ";
char_t *iter = str;                 /* iter == "áéíóúÄ" */
iter = unicode_next(iter, ekUTF8);  /* iter == "éíóúÄ" */
iter = unicode_next(iter, ekUTF8);  /* iter == "íóúÄ" */
iter = unicode_next(iter, ekUTF8);  /* iter == "óúÄ" */
iter = unicode_next(iter, ekUTF8);  /* iter == "úÄ" */
iter = unicode_next(iter, ekUTF8);  /* iter == "Ä" */
iter = unicode_next(iter, ekUTF8);  /* iter == "" */
iter = unicode_next(iter, ekUTF8);  /* Segmentation fault!! */
str

Cadena Unicode.

format

Codificación de str.

Retorna

Puntero al siguiente carácter de la cadena.

Observaciones

No verifica el final de la cadena. Debemos detener la iteración cuando codepoint == 0.


unicode_back ()

Retrocede al carácter anterior de una cadena Unicode.

const char_t*
unicode_back(const char_t *str,
             const unicode_t format);
str

Cadena Unicode.

format

Codificación de str.

Retorna

Puntero al carácter anterior de la cadena.

Observaciones

No verifica el inicio de la cadena.


unicode_isascii ()

Comprueba si codepoint es un carácter US-ASCII 7.

bool_t
unicode_isascii(const uint32_t codepoint);
codepoint

El código Unicode del carácter.

Retorna

Resultado del test.


unicode_isalnum ()

Comprueba si codepoint es un carácter alfanumérico.

bool_t
unicode_isalnum(const uint32_t codepoint);
codepoint

El código Unicode del carácter.

Retorna

Resultado del test.

Observaciones

Solo tiene en cuenta caracteres US-ASCII.


unicode_isalpha ()

Comprueba si codepoint es un carácter alfabético.

bool_t
unicode_isalpha(const uint32_t codepoint);
codepoint

El código Unicode del carácter.

Retorna

Resultado del test.

Observaciones

Solo tiene en cuenta caracteres US-ASCII.


unicode_iscntrl ()

Comprueba si codepoint es un carácter de control.

bool_t
unicode_iscntrl(const uint32_t codepoint);
codepoint

El código Unicode del carácter.

Retorna

Resultado del test.

Observaciones

Solo tiene en cuenta caracteres US-ASCII.


unicode_isdigit ()

Comprueba si codepoint es dígito (0-9).

bool_t
unicode_isdigit(const uint32_t codepoint);
codepoint

El código Unicode del carácter.

Retorna

Resultado del test.

Observaciones

Solo tiene en cuenta caracteres US-ASCII.


unicode_isgraph ()

Comprueba si codepoint es un carácter imprimible (excepto el espacio blanco ' ').

bool_t
unicode_isgraph(const uint32_t codepoint);
codepoint

El código Unicode del carácter.

Retorna

Resultado del test.

Observaciones

Solo tiene en cuenta caracteres US-ASCII.


unicode_isprint ()

Comprueba si codepoint es un carácter imprimible (incluido el espacio blanco ' ').

bool_t
unicode_isprint(const uint32_t codepoint);
codepoint

El código Unicode del carácter.

Retorna

Resultado del test.

Observaciones

Solo tiene en cuenta caracteres US-ASCII.


unicode_ispunct ()

Comprueba si codepoint es un carácter imprimible (expecto el espacio blanco ' ' y alfanuméricos).

bool_t
unicode_ispunct(const uint32_t codepoint);
codepoint

El código Unicode del carácter.

Retorna

Resultado del test.

Observaciones

Solo tiene en cuenta caracteres US-ASCII.


unicode_isspace ()

Comprueba si codepoint es un carácter de espaciado, nueva línea, retorno de carro, tabulador horizontal o vertical.

bool_t
unicode_isspace(const uint32_t codepoint);
codepoint

El código Unicode del carácter.

Retorna

Resultado del test.

Observaciones

Solo tiene en cuenta caracteres US-ASCII.


unicode_isxdigit ()

Comprueba si codepoint es un dígito hexadecimal 0 1 2 3 4 5 6 7 8 9 a b c d e f A B C D E F.

bool_t
unicode_isxdigit(const uint32_t codepoint);
codepoint

El código Unicode del carácter.

Retorna

Resultado del test.

Observaciones

Solo tiene en cuenta caracteres US-ASCII.


unicode_islower ()

Comprueba si codepoint es una letra minúscula.

bool_t
unicode_islower(const uint32_t codepoint);
codepoint

El código Unicode del carácter.

Retorna

Resultado del test.

Observaciones

Solo tiene en cuenta caracteres US-ASCII.


unicode_isupper ()

Comprueba si codepoint es una letra mayúscula.

bool_t
unicode_isupper(const uint32_t codepoint);
codepoint

El código Unicode del carácter.

Retorna

Resultado del test.

Observaciones

Solo tiene en cuenta caracteres US-ASCII.


unicode_tolower ()

Convierte una letra a minúscula.

uint32_t
unicode_tolower(const uint32_t codepoint);
codepoint

El código Unicode del carácter.

Retorna

La conversión a minúscula si la entrada es una letra mayúscula. De lo contrario, el mismo codepoint.

Observaciones

Solo tiene en cuenta caracteres US-ASCII.


unicode_toupper ()

Convierte una letra a mayúscula.

uint32_t
unicode_toupper(const uint32_t codepoint);
codepoint

El código Unicode del carácter.

Retorna

La conversión a mayúscula si la entrada es una letra minúscula. De lo contrario, el mismo codepoint.

Observaciones

Solo tiene en cuenta caracteres US-ASCII.

❮ Anterior
Siguiente ❯