Usando caracteres UNICODE, sin importar el encodamiento del script en Db2

By Andres Gomez Casanova posted Jan 31, 2019 08:36 AM

  

En la actualidad reconocemos una gran variedad de idiomas, ya sean lenguas vivas o lenguas muertas, donde muchos de estos idiomas tienen su propio conjunto de caracteres. El hecho de poder manipular mensajes en cualquier idioma ha sido uno de los desafíos informáticos desde sus orígenes, y con el continuo reconocimiento de nuevos caracteres esta tarea se vuelve aún más compleja. Para esto se han definido varias formas de representar múltiples tipos de caracteres.

La tabla ASCII (básica) fue una de las primeras aproximaciones para representar un idioma en donde se definía un conjunto de caracteres enfocado al idioma inglés, por ser una definición para Estados Unidos, un país angloparlante. Para esto, se tomaron 7 bits y se comenzó a asignar un valor para los caracteres del inglés, más unos caracteres de símbolos y para caracteres de control (para transmisión – inicio de transmisión, fin de transmisión; o para impresión – salto de línea, retorno del carro). Todo esto en las 128 posibles combinaciones que daban los 7 bits.

Posteriormente hubo necesidad de más caracteres para otros idiomas y comenzaron a adaptar la tabla ASCII a otros contextos, donde la extendieron en un bit más. En este momento ya había caracteres de varias lenguas romances, helénicas y germánicas – español, francés, griego, alemán, y de esta forma se podía representar las lenguas europeas de mayor uso. Aquí, la tabla ASCII (extendida) manejaba 8 bits o 256 caracteres.

Pero esto no era suficiente, ya que aún faltaba representar otros idiomas con caracteres muy diferentes, como el hebreo, cirílico o árabe, o idiomas donde se manejan silabarios como en el japonés o pictogramas como en el mandarín. En fin, faltaban muchos caracteres a representar, y además tener un mecanismo flexible para representar nuevos símbolos, como el de la moneda Euro que salió en 1995 o el del copyleft en 2016.

Para resolver este problema, varias de las grandes empresas de tecnología se reunieron en 1991 y crearon la tabla UNICODE. Ya han sacado varias versiones de esta tabla, y cada vez contiene más caracteres tipográficos (glyphs), donde cada caracter se representa con un número hexadecimal. La tabla comenzó con más de 7 000 caracteres y a 2018, la tabla ya tenía más de 137 000. Por lo tanto, se debía usar más de un byte para representar tal cantidad de posibles caracteres. Pero el propósito de esta tabla es identificar los caracteres, más no trata propiamente sobre la problemática de la implementación de la tabla en sistemas informáticos.

La representación de la tabla UNICODE en sistemas informáticos implica un gran desafío, y por este motivo han sacado varios "encodamientos" o codificaciones, entre esos UTF-32, UTF-16, UTF-8, cada uno con un mecanismo distinto para representar los miles de caracteres. Sin embargo, el encoding UTF-8 puede considerarse como el mejor mecanismo por ser efectivo para representar la tabla UNICODE ya que utiliza pocos bytes para los caracteres más frecuentemente usados. Recordemos, que los 127 caracteres de la tabla ASCII básica son los mismos 127 primeros caracteres en la tabla UNICODE.

Cuando se manejan múltiples sistemas operativos (Windows, Linux, MacOS) el encodamiento puede no ser el mismo. Por ejemplo, en Windows muchos archivos tienen encodamiento ISO-8859 pero esta tabla no representa toda la tabla UNICODE, sino un leve subconjunto. Por lo anterior, cuando se transfieren archivos entre diferentes sistemas con diferentes encodamientos, los caracteres superiores al 127 se ven como caracteres “raros” y hay una tendencia a que los scripts no funcionen adecuadamente.

Para evitar esto, se recomienda que siempre se escriban los scripts SQL PL de Db2 con caracteres de la tabla ASCII básica. Si es imperativo referirse a caracteres que no están en este conjunto, entonces se deben describir por su código UNICODE, el valor hexadecimal. El lenguaje SQL PL está definido con solo caracteres en inglés (Create Table, Drop Procedure, Insert Into) los cuales están en la tabla ASCII básica. En cambio, las cadenas de caracteres a las cuales puede hacer referencia si pueden contener otros caracteres UNICODE, como las que hay en un Insert, Update o un predicado Where. Para esto se precede el inicio de la cadena con el carácter U seguido de un símbolo "Et" o Ampersand en inglés (U&) e indicando la secuencia de escape para cada carácter UNICODE.

A continuación, se muestra un ejemplo de cómo escribir de dos formas la siguiente frase: "Andrés Gómez habla Español". Esta frase contiene los siguientes caracteres que no hacen parte de la tabla ASCII básica, una tilde e - é, tilde o - ó, eñe - ñ. Para hacer referencia a estos caracteres se van a escribir las "constantes" como se definen en la documentación de Db2 y se buscan sus códigos en la tabla UNICODE. Para el ejemplo, se va a crear una tabla y analizar cómo queda la información almacenada.

 

db2 "create table unicode (description varchar(64))"

db2 "insert into unicode values
(U&'Andr\00e9s G\00f3mez habla Espa\00F1ol')"

db2 "insert into unicode values
(U&'Andr@00e9s G@00F3mez habla Espa@00f1ol' UESCAPE '@')"

db2 "insert into unicode values
(U&'Andr\+0000e9s G\+0000f3mez habla Espa\+0000F1ol')"

db2 "insert into unicode values
(U&'Andr@+0000e9s G@+0000F3mez habla Espa@+0000f1ol' UESCAPE '@')"

db2 "select distinct hex(description) from unicode"
416E6472C3A9732047C3B36D657A206861626C612045737061C3B16F6C
 

En el ejemplo anterior, se ha subrayado la secuencia de escape de caracteres, donde podemos ver que hay dos formas de hacerlo. Una forma es precediendo el código UNICODE con una barra inversa o backslash. La otra forma es más larga donde se selecciona un caracter de escape que se define por UESCAPE después de la cadena, en este caso la arroba; dentro de la cadena, se precede el caracter UNICODE con el caracter de escape. En general, si el caracter a representar tiene más de 4 dígitos hexadecimales (los primeros 65565 caracteres), se puede preceder el código con el signo más para representar un caracter de 6 dígitos hexadecimales (hasta 16 millones).

Por otro lado, se puede caer en la tentación de concatenar el código UNICODE en hexadecimal, pero la cadena resultante no representa caracteres UNICODE.

 

db2 "insert into unicode values 
('Andr' || X'e9' || 's G' || X'f3' || 'mez habla Espa' || X'f1' || 'ol - No Unicode')"

db2 "select hex(description) from unicode"
416E6472E9732047F36D657A206861626C612045737061F16F6C202D204E6F20556E69636F6465

 

La diferencia de las dos cadenas en hexadecimal es el valor C3 antes de cada caracter UNICODE.

Retomando el problema de encodamiento, un archivo que contenga caracteres superiores a la tabla ASCII básica se puede convertir recodificando el archivo, indicando cuál es el encodamiento de origen y de destino por medio de comandos en Linux como: 

Pero para poder recodificarlos es necesario conocer cuál era el encodamiento original.

Los caracteres en español que no hacen parte de la tabla ASCII básica tienen los siguientes códigos en la tabla UNICODE:

gomes-unicode-1.png 

Para concluir, la mejor práctica al hacer scripts en SQL PL es escribir los caracteres superiores al ASCII 127 escapándolos e indicando su código en la tabla UNICODE. Esto evitara encodamientos diferentes, y todo tipo de problemas con caracteres "extraños" al transferir archivos entre diferentes plataformas. Con esto nos asegura que no importa el entorno de ejecución, el script se procesará de la misma manera.

 

Referencias:

 

 


#espanol
0 comments
2 views

Permalink