16 de Julio, 2005

Convertir entre codificaciones en nuestros programas

Contaba cómo me puse a trastear con infrarrojos en OpenBSD, y que acabé programando una pequeña herramienta para extraer la información de los objetos OBEX que recibía en el portátil desde mi Philips 755.

El principal problema que me encontré, una vez localizada la documentacíon de OBEX 1.2, es que los chunks del tipo name, que son los que indican al receptor como llamar a los datos que se encapsulan en el objeto, están codificados con UNICODE.

Ya hablé de codificaciones ISO 8859-1 (aka latin1) y UNICODE. Como en este caso no quería calentarme demasiado la cabeza, pues descarté esa información.

Esta semana me puse un par de ratos a estudiar el tema para ver como convertir esa cadena en UNICODE a la codificación que esté usando el sistema ejecutando ext_odata (README), de forma que la herramienta guardara en disco los datos con el nombre que se indica en el mencionado chunk.

He optado por emplear libiconv, aunque en realidad es algo más complicado que eso ;).

Resulta que iconv ha sido incluido en glibc, que es la librería disponible en los sistemas GNU/Linux (de estándar C, y más cosas). El proyecto mencionado se encarga de proporcionar una implementación de iconv para los sistemas que no disponen de una (como los BSD, su libc no dispone de esa función).

Así que la aplicación lleva soporte para compilarse sin soporte iconv (como hasta ahora), con soporte nativo en Linux, y empleando la implementación portable de GNU. Pues me ha costado un rato darme cuenta del matiz entre las dos últimas posibilidades ;).

Me voy a referir a la documentación de la versión portable, porque no he encontrado las páginas de manual de la versión nativa de glibc (?). Hay alguna diferencia, pero bueno... de eso ya se encargará el compilador de C (-Wall ayuda bastante, el primer argumento de iconv no es const en la versión nativa; por ejemplo).

Es bastante sencillo: obtenemos un descriptor para la traducción, traducimos lo que necesitemas y, finalmente, liberamos el descriptor. Es similar a cómo manejaríamos un fichero.

#include<iconv.h>
...

iconv_t ic;
char *pt1, *pt2;
size_t origlen, destlen;
...

pt1=strOrig;
pt2=buffDest;

origlen=TAM_ORG;   /* tam de la cadena origen */
destlen=MAX_NAME;  /* tam del buffer destino */

ic=iconv_open("//IGNORE" ,"UTF-16BE");
iconv(ic, (const char **)&pt1, &origlen, &pt2, &destlen);
iconv_close(ic);

Este es el uso que he hecho en la aplicación. Tengo en strOrig un vector de caracteres de tamaño TAM_ORG (no es una cadena de texto de C, ojo), y en buffDest un buffer donde irá a parar la cadena convertida, poniendo en destlen su tamaño.

Vamos a convertir de UTF-16BE (Big Endian emplea OBEX; NO lo pone en la documentación, y ojo que la versión nativa de iconv usa LE por defecto para UTF-16 y la portable trabaja con BE si no decimos nada :o), al juego de caracteres local (se especifica con "" -cadena vacía-, aunque yo he puesto el //IGNORE para que ignore los caracteres que no sepa como convertir).

La única dificultad, en este caso, es que he insistido en que la aplicación fuera portable, pero nada que no se pueda conseguir con un poco de esfuerzo y algunos #ifdef ;).

En fin, resulta muy útil esta librería. Es fácil de utilizar y evita que tengamos que implementar nuestras propias rutinas (bastante complejas). Así que ahora mi herramienta guarda los datos contenidos en el objeto con el nombre adecuado, tanto en Linux como en otros tipo UNIX que tengan libiconv instalada :).

Anotación por Juan J. Martínez.

Los comentarios están cerrados: los comentarios se cierran automáticamente una vez pasados 30 días. Si quieres comentar algo acerca de la anotación, puedes hacerlo por e-mail.