La marca BOM en UTF-8

Facebooktwittergoogle_pluslinkedinmailFacebooktwittergoogle_pluslinkedinmail

Este artículo es un pequeño truquillo basado en mi experiencia en situaciones concretas. Veréis que, desde el punto de vista de la programación es algo muy simple que, si lo piensas un segundo, seguramente ya conoces. Sin embargo, para aquellos que se lo encuentran por primera vez puede suponer un pequeño quebradero de cabeza. Te cuento. Todos, a día de hoy, trabajamos con la codificación UTF-8 en nuestros archivos. Todos nuestros scripts están grabados con esta codificación. Y también la damos (y la exigimos) cuando tenemos que intercambiar datos con servicios o aplicaciones de otros desarrolladores. Esta codificación abarca todo el código Unicode y evita que tengamos conflictos cuando empleamos caracteres de alfabetos locales o, incluso, guarismos que no pertenecen a ninguna alfabetización específica.

Cuando digo que “todos usamos UTF-8” estoy generalizando un poco. En determinados proyectos de gran envergadura, con recursos y finalidades muy específicos, se emplean otras codificaciones (y si no, que le pregunten a IBM, que de eso saben un rato) pero, desde luego, en el mundillo del desarrollo web, o de aplicaciones de escritorio, UTF-8 es, sin duda, el estándar imperante.

Sin embargo, hay dos modos de grabar contenidos o documentos en UTF-8. Con y sin BOM. Y ¿qué es el BOM? Se trata de una marca específica que determina el inicio de un contenido en UTF-8. Puede servirnos para acotar flujos de datos, pero también puede “contaminar” nuestros resultados, si leemos “algo” en este formato y nos encontramos inesperadamente esa marca (por cierto, BOM es el acrónimo de Byte Order Mark, uno más de los miles de anglicismos que nos aprendemos sin más, y no tiene más importancia).

QUÉ ES EL BOM

Cómo ya hemos comentado, es una marca que delimita el inicio de un contenido en UTF-8. En realidad se trata de una secuencia de tres códigos hexadecimales (0xEF, 0xBB y 0xBF). Tus contenidos en UTF-8 pueden tener esta marca, o no tenerla. El problema aparece cuando lees contenidos que la tienen, y luego tratas de volcarlos a un archivo, o a una base de datos, o a una pantalla. De repente, precediendo el contenido, te aparecen unos símbolos extraños () que no sabes de donde han salido. Tu lees con un script PHP, por ejemplo, los datos de un archivo CSV que te han mandado, y los sacas a pantalla. Delante del primer dato te aparecen esas marcas, y no sabes de donde salen. Piensas que “se han colado” en el CSV, lo abres con tu editor de texto, y no están por ningún sitio. Intentas eliminar esos caracteres con un str_replace() (por ejemplo) y no se eliminan. Siguen apareciendo, estropeando tu volcado.

Suponte que tienes un archivo CSV con, digamos, nombres de personas y sus ciudades de residencia. Podría ser algo como este archivo llamado personas.csv:

Cómo ves, es la notación CSV típica, con un registro por línea física y los datos de dicho registro separados entre sí por ; (en realidad, que el separador sea ;, , o el que sea es irrelevante, mientras haya un acuerdo claro entre el servicio que suministra los datos y el que se nutre de ellos).

Ahora vamos a hacer un script para leer estos datos línea a línea (lo que, implícitamente, significa registro a registro) y volcarlos a la pantalla. Lo llamamos leer_1.php:

Cómo ves, leemos cada línea del CSV con fgetcsv(). Si no estás familiarizado con esta función puedes ver su sintaxis detallada en este enlace. Tras leer cada registro, lo volcamos a pantalla. El resultado es limpio y correcto:

EL PROBLEMA

Mientras el fichero CSV esté codificado en UTF-8 sin BOM, y tu página emplee la misma codificación, no tendrás ningún problema en mostrar correctamente los resultados. Pero ahora supongamos que el fichero CSV está en UTF-8 con BOM (lo que no es, desde luego, algo inhabitual, si no, más bien, lo contrario). Y suponte que el navegador del usuario (o el propio documento web) está configurado en otra codificación. Lo que te vas a encontrar es esto:

Observa que en el primer nombre (esto es, al principio del fichero) se muestra la visualización en ISO del BOM. Quizá pienses que la solución es hacer un srt_replace() “a pelo”, sustituyendo la línea siguiente:

echo "Nombre: ".$persona[0].'<br>';

por:

echo "Nombre: ".str_replace('', '', $persona[0]).'<br>';

Pruébalo. En seguida verás que no funciona. El resultado sigue saliendo defectuoso.

LA SOLUCIÓN

Lo primero que tenemos que hacer es usar la función pack() de PHP para empaquetar las tres secuencias hexadecimales que componen la marca BOM en una variable con un formato específico. Lo hacemos así:

$bom = pack("CCC", 0xEF, 0xBB, 0xBF);

Esta variable SÍ podemos usarla con str_replace(). Observa leer_2.php:

Con esto tienes resuelto el problema. El primer nombre, que es el que tiene la marca BOM es afectado por el cambio, apareciendo limpio.. Los demás, no se ven modificados en modo alguno, ya que un fichero codificado en UTF-8 con BOM sólo tiene una de estas marcas, al principio del fichero.

CONCLUYENDO

Cómo ves, resolver este problema es muy fácil. Si tienes curiosidad por el funcionamiento de pack() puedes revisar este enlace. Con el tiempo verás que este problema se presenta mucho en ficheros enviados mediante un formulario, o leídos desde una fuente externa, y almacenados en el servidor para su procesamiento. Cómo ves, se trata de un truco muy sencillo, pero que te soluciona un problema molesto.

     

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *