miércoles, 6 de agosto de 2008

Referencias a caracteres en XML de vuelta a unicode

Los caracteres que no son son representables en algún encoding pueden representarse con su codificación en XML. Si c es un caracter, la expresión Python para generar esta representación es:
"&#%d;" % ord(c)

Cuando codificamos un texto, podemos especificar qué hacer con los caracteres que no se pueden representar en el encoding usado. Con el argumento 'ignore', simplemente no se incluyen, con 'replace' son reemplazados por un '?' y usando 'xmlcharrefreplace' obtenemos la mencionada codificación. Por ejemplo:
codificado = texto.encode(encoding, "xmlcharrefreplace")

Para recuperar la cadena original, no encontré nada haciendo una simple búsqueda, así que lo solucioné de la siguiente manera, usando una expresión regular y asumiendo que la cadena puede ser representada en el encoding por defecto (en mi caso UTF-8).

La expresión regular simplemente matchea con la forma de la representación XML, el objeto match es pasado una función que toma la parte que corresponde al número, la convierte a entero y devuelve el resultado de aplicarle unichr a ese valor. La función unichr devuelve el caracter unicode correspondiente a un número.
def unicodechar(match):
return(unichr(int(match.group(1))))

def replace_xmlrefs(string):
return re.sub("&#(\d+);",unicodechar,string)


Nada complejo, y se lo puede ver en funcionamiento:

>>> encoded = "財団 基金会"
>>> import re
>>> replace_xmlrefs(encoded)
u'\u8ca1\u56e3 \u57fa\u91d1\u4f1a'
>>> print replace_xmlrefs(encoded)
財団 基金会

La última línea, donde se imprimen los caracteres representados puede fallar si la consola no soporta esos caracteres, o no estamos usando un encoding que cubra todo unicode. Lo recomendable es usar UTF8.
Se puede iniciar el intérprete python especificando que use otro locale (vemos los disponibles con locale -a).
Por ejemplo:
LC_ALL=es_AR.utf8 python


Obviamente siempre hay gente que hace mejor las cosas, y acá encontré una forma de hacer esto que considera también los casos en que la referencia tiene un número hexadecimal.

Update 15-08-2008:
Este snippet también reemplaza las entidades HTML.

No hay comentarios: