+34 687 770 998 hector.ayestaran@gmail.com





Blog


01
OCT
2013

Utilizando y entendiendo la codificación UTF-8

Muchas veces me he preguntado cuál sería el impacto (positivo) sobre nuestra economía si Gobierno y Real Academia de la Lengua aboliesen todos los caracteres problemáticos tales como tildes y eñes. La productividad que ganaríamos los profesionales de este sector podría hacer más competitivos nuestros productos y servicios, dándoles un pequeño empujón al resto de sectores, en esta civilización informatizada.

Pero como eso es demasiado pedir dadas las circunstancias, nos tenemos que resignar a codificar correctamente los datos de nuestras aplicaciones, y de eso precisamente va a tratar este artículo. Utilizaremos como contexto el desarrollo web, debido a lo acuciante de la cuestión en dicho subsector, y dividiremos lo expuesto en cinco partes: Qué es una codificación de caracteres, datos estáticos y documentos HTM/HTML, datos dinámicos y bases de datos, datos enviados por POST, y datos enviados por email.

Qué es una codificación de caracteres

Un sistema de codificación de caracteres o charset, es una tabla de códigos o valores, que empareja caracteres o símbolos con una serie de bits, octetos o impulsos eléctricos, y está orientado a la transmisión telématica o almacenamiento de datos. En este artículo abordaremos los dos más utilizados en el mundo occidental: ISO-8859-1 y UTF-8.

ISO-8859-1



También llamado Latin1, este charset pertenece a los estándares ISO, e incluye todos los caracteres utilizados por los idiomas escritos en caracteres latinos. Entre estos caracteres tenemos los imprimibles que pertenecen al subconjunto ASCII, y los imprimibles que no. Estos últimos son los llamados problemáticos, normalmente caracteres diacríticos, tales como tildes, etc.

La siguiente tabla (tabla 1, tomada de Wikipedia) muestra la codificación de dichos caracteres problemáticos, siendo las tres primeras columnas el código del caracter en distintos sistemas, octal, decimal y hexadecimal:
Nótese que a la ñ le corresponde el valor 241 / F1. Los códigos de ISO-8859-1 son compatibles con ASCII, podemos comprobarlo con PHP mediante echo chr(241); o echo utf8_encode(chr(241));

UTF-8



Uno de los tres formatos del charset Unicode, incluye caracteres de la mayoría de lenguajes del mundo, incluídos glifos e ideogramas. Convertido en codificación estandar con el paso del tiempo, deberíamos utilizarlo en todos nuestros proyectos, ya que nunca sabemos con certitud hasta dónde llegarán las cosas, y con qué tipos de caracteres tendremos que lidiar.

La siguiente tabla (tabla 2, tomada de http://www.utf8-chartable.de) muestra algunos de los caracteres de la anterior tabla, pero en codificación UTF-8, siendo la tercera columna el código hexadecimal de los caracteres codificados:

Nótese aquí también que a la ñ le corresponden dos valores hexadecimales: C3 y B1, que transformados a decimal es obtenido el código ASCII de los caracteres: Ã y ±, con los cuales nos vamos a familiarizar a lo largo del artículo.

Datos estáticos y documentos HTM/HTML

Lo primero que debemos hacer es cerciorarnos de que en el proyecto web que hemos creado en nuestro IDE, hemos seleccionado UTF-8 como la codificación predeterminada. Si esto no es así, todo el texto de los archivos que creemos y guardemos será codificado con otro charset, y los caracteres problemáticos podrían no verse correctamente en el navegador. Si da la casualidad de que trabajamos con un proyecto viejo, codificado en ISO-8859-1 por ejemplo, podemos transformar los archivos a UTF-8 automáticamente mediante el siguiente software: UTFCast.

Posteriormente nos aseguraremos de que en nuestro servidor web, en este caso Apache, el default charset especificado sea el mismo, UTF-8. Esto lo podemos ver, si tenemos acceso como administrador del servidor, en la directiva AddDefaultCharset del fichero httpd.conf, o también, en el panel de control de nuestro dominio. En el caso de que ningún charset esté especificado, el servidor web no enviará la siguiente cabecera HTTP (sí en cambio el resto de cabeceras y el código cliente de página):

Content-Type: text/html; charset=utf-8

la cual hubiera indicado al navegador en qué charset debería estar codificada la página, y este último (el navegador) aplicará su default charset, que por ejemplo en Firefox 12 por defecto es ISO-8859-1. Si dicho default charset coincide con la codificación de la página servida, los caracteres problemáticos se verán bien, en caso contrario saldrán rombos con un interrogante o caracteres de tipo Ã: Volviendo a lo anterior, si el servidor web no envía la cabecera HTTP citada, podemos recurrir a dos cosas: En el caso de haber conflicto entre ambas, la segunda, la directiva del .htaccess, prevalecerá.

Por el contrario, si el servidor web tiene definido un default charset, enviará la cabecera HTTP correspondiente, y si la codificación de nuestra página no coincide con la de dicha cabecera, pasará lo mencionado anteriormente (rombos o Ã). Esto lo podemos solucionar mediante la segunda opción, la directiva en el .htaccess, especificando un default charset que coincida. La primera opción no serviría de nada.

Todo esto no será así si nuestra página tiene extensión PHP, ya que en tal caso prevalecerá únicamente el default charset definido en la directiva default_charset del archivo php.ini, tal y como veremos a continuación.

Datos dinámicos y bases de datos

Una vuelta de tuerca más. Esta vez tenemos dos nuevos actores en el escenario, nuestro cliente y nuestro servidor de bases de datos, que en este ejemplo serán MySQL. Hemos dicho anteriormente que ahora el charset que manda es el definido en nuestro php.ini. Tendremos que guardar nuestra página de acorde a este charset, pero también deberemos tener en cuenta la codificación especificada en nuestra base de datos.

Antes de nada vamos a aclarar una serie de conceptos sobre la configuración del servidor MySQL, a la cual accederemos desde el Workbench, concretamente Server Administration > Nuestra instancia > Options file.

Veamos la siguiente pantalla:

Cabe mencionar que si todo va bien, los datos obtenidos de la base de datos llegarán al flujo de nuestro programa PHP codificados, y tal codificación deberá coincidir con la directiva default_charset de nuestro php.ini, de lo contrario no veremos bien los caracteres problemáticos.
Los datos estarán codificados, y no deberemos ejecutar un utf8_decode o algo análogo después de obtenerlos. Quedarán "acompañando" a los datos estáticos, los cuales estarán codificados con el mismo charset ya que los guardamos así, y será el navegador quien se encargue de decodificarlos a todos una vez lleguen a la máquina cliente.
Si los charsets no coinciden, veremos rombos o caracteres de tipo Ã, según las circunstancias mencionadas en el anterior apartado.

Cabe mencionar también que ante escenarios catastróficos, como por ejemplo que los datos en la base de datos "tal y como se ven" se vean codificados, porque por alguna causa desconocida dentro están así, es decir, que por ejemplo en vez de ver "leña" veamos "leña", el funcionamiento entre cliente y servidor podría ser imprevisible, y además podría diferir según qué cliente estuviésemos utilizando.

A la hora de insertar datos en la base de datos, si los charsets no coinciden ocurrirá lo siguiente: Si en vez de obtener los datos de una base de datos, los obtenemos del disco duro, la lógica imperante será la misma. El charset con el que se guardó el archivo leído deberá coincidir con el del php.ini.

Datos enviados por POST

Cuando enviamos datos mediante la etiqueta form, no debemos preocuparnos por nada ya que nuestro navegador automáticamente codificará los datos con el default charset de nuestro php.ini, y después les dará formato urlencode, esos datos hexadecimales precedidos por un porcentaje que vemos en las URLs de nuestro navegador.

Por ejemplo, si enviamos un: <input name="miCampo" value="ñ" />

nuestro navegador enviará al servidor web las siguientes cabeceras y contenido:

POST /documento.php HTTP/1.1
Host: www.programadorfreelance.es
User-Agent: Mozilla/5.0 (Windows NT 5.1; rv:12.0) Gecko/20100101 Firefox/12.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 8

miCampo=%C3%B1

y el servidor web al recibir el campo le hará un urldecode automáticamente, pero el dato seguirá codificado en UTF-8.

Nótese que el valor de miCampo es %C3%B1, siendo C3 y B1 los valores hexadecimales de ñ en la codificación UTF-8 (véase la tabla 2). Si el default charset del php.ini fuese ISO-8859-1, el valor de miCampo sería %F1 (véase la tabla 1).

La cosa cambia si el POST lo enviamos mediante Javascript, con jQuery por ejemplo, y el default_charset de nuestro php.ini es ISO-8859-1, y el dato va a ser mostrado en pantalla. En tal caso tendremos que realizar un utf8_decode en el servidor al recoger el dato, algo así:

$miCampo = utf8_decode($_POST['miCampo']);

¿Por qué esto? jQuery por defecto utiliza UTF-8 y urlencode, y si especificamos otro charset lo omitirá, al menos hasta la versión 1.8.

El código para realizar un post con jQuery es el siguiente:


y enviará al servidor web las mismas cabeceras y contenido que mediante la etiqueta form, siendo excepción las siguientes:

Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest


Datos enviados por email

Gran parte de lo visto hasta ahora es aplicable al envío de un email. Tanto el asunto como el cuerpo del email deberán estar codificados según el charset elegido para la comunicación SMTP, pudiendo ver dicho charset en el código fuente del email al recibirlo (lo guardamos en disco con extensión EML y lo abrimos con el bloc de notas), concretamente en la cabecera:

Content-Type: text/html; charset="utf-8"

y pudiendo forzarlo (el charset) de dos maneras: Una vez más, la codificación de cualquier dato estático o dinámico, ya sea de formulario, de base de datos, o de disco, que vaya a componer el asunto o cuerpo del email enviado, deberá coincidir con la elegida para la comunicación SMTP, y será el programa de correo del destinatario quien se encargue de decodificarlo. Si los charsets no coinciden, podrá ocurrir lo siguiente:

Aquí concluye este artículo. Espero que sirva para ahorrar a los lectores más de un quebradero de cabeza.

Volver