Saturday 6 August 2011

Solución a carácteres raros y juegos de carácteres (charset)


Si al visitar tu propia web en tu navegador ves "carácteres raros", puede ser debido a dos cosas:

  • realmente hay carácteres "binarios"
  • hay un conflicto de "juego de carácteres" o "charset" en inglés

Carácteres binarios


Si tienes esos carácteres binarios es porque tú o algún visitante o administrador de tu web introdujo en el sistema (en un archivo o en la base de datos a través de un formulario) alguna cadena de texto COPIANDO Y PEGANDO desde algún software de Microsoft, por ejemplo. No siempre que se haga eso tiene que ocurrir tal "desajuste" de carácteres, pero la mayoría de las veces que ocurre es por esa razón. es algo que te puede ocurrir aunque tengas perfectamente construida tu web a nivel de charsets.

La solución a eso es disponer de alguna función PHP de limpieza de esos posibles carácteres, usándola justo cuando recibas datos a través de formularios, para que así se te guarden limpios en la base de datos.

En este otro artículo comparto esta función para limpiar, aunque ya aviso de que solo funciona si estás trabajando con el charset "utf-8" en toda tu web (base de datos, HTML y archivos!):

http://crear-paginas-web.blogspot.com/2011/06/eliminar-caracteres-raros-o-binarios-de.html


Conflictos de charset


Hay que definir el charset de tres elementos de tu aplicación web:
  • la base de datos
  • el HTML devuelto al navegador
  • los archivos del servidor (ya sean PHP o HTML o de texto)
Para empezar, la base de datos tiene dos tres aspectos en los que hay que configurar el charset, y no necesariamente coincidente (por eso son dos!).
  1. Uno es el charset utilizado para guardar los datos en sí.
  2. El otro el de "cotejamiento" de los datos. No me preguntes muy bien cuál es la diferencia... para eso busca por internet. Lo que te digo es que ahí ya puede haber una primera fuente de problemas!
  3. El tercero es la conexión del PHP con el MySQL, que también tiene que indicar el charset que se quiere usar. Es tan sencillo como añadir el siguiente comando después de cada conexión con MySQL:
    1 <?php
    2
    3 $connection
    = mysql_connect($db_host,$db_user,$db_passw) ;
    4
    mysql_set_charset('utf8');
    5
    6
    ?>

[* añadido el 6 de Dic. de 2011]

Lo recomendable es que ambos coincidan y que sea "UTF-8 unicode". Los demás charset suelen estar ahí para compatibilizar con bases de datos antiguas, pero hoy en día se recomienda UTF-8 UNICODE como charset completamente universal! puede contener carácteres chinos, árabes, rusos, latinos, etc... y todos mostrarse en un mismo párrafo perfectamente! ;)

La segunda cuestión es el charset de la página web devuelta al navegador. Es decir, el charset declarado en el meta-tag del HTML: "content-type", que indica al navegador con qué charset debe renderizar el contenido de tu HTML. Y de nuevo, lo recomendable es que uses UTF-8, así:

1 <meta equiv="Content-Type" content="text/html; charset=UTF-8" />

El tercer elemento de tu aplicación web en el que tienes que definir el charset es el más sútil y a veces problemático! Se trata del charset usado para el contenido de tus archivos en el servidor (PHP, texto, HTML, etc...). Y digo que es problemático porque solo de unos años para acá los editores de texto o de código que usamos en el escritorio de nuestros PCs (sea Linux o Windows...) han ido incorporando más funciones para poder definir el charset de nuestros documentos de una forma más clara y controlada para el usuario. Y aún así a veces yo tengo problemas!

El problema en este punto es que por ejemplo algunos de tus archivos .php estén codificados en iso-8859-1 y tu base de datos en utf-8. ¿Porqué? pues porque entonces es un poco imprevisible como se renderizarán algunos carácteres, como por ejemplo el símbolo de euro, que no tiene una codificación propia en iso-8859-1. ¿Ya vas entendiendo el lío que se puede montar?

Por esa razón hemos de usar un editor de texto/código que nos permita saber en todo momento el charset de nuestros documentos y modificarlo a voluntad. Es algo que cada día se ve más, pero aún no acaba de funcionar bien.

Por ejemplo, en Linux yo uso el excelente editor opensource Bluefish. Pero en la última versión parece tener un bug según el cuál te abre un documento con el último charset que usaste!!! imagínate que lío!!! si no voy con cuidado y tengo webs que usen iso-8859-1 y otras utf-8, si no me fijo me puede ir mezclando los charset entre ellos!!!! cosa que ya me ha ocurrido, porque aún tengo unos cuantos sites que tengo que convertir totalmente a utf-8 :(((

En fin, creo que queda evidente el problemón... símbolos de euros que no aparecen, vocales acentuadas que se ven como ~A- o cosas peores...

Conclusión


Es muy sencilla la moraleja a todo lo anterior:
  • usa UTF-8 para tus archivos
  • usa UTF-8 para tu base de datos
  • usa UTF-8 como charset en el meta-tag de tus páginas HTML
-->

7 comments:

  1. Me parece muy interesante este artículo ya que es bueno estar al tanto con todo lo relacionado a este tema.

    ReplyDelete
  2. Gracias por el feedback positivo. Es alentador. Tengo pendiente escribir un poco más sobre el tema e incluso compartir un par de scripts que me he hecho cogiendo de aquí y de allá, en cuanto al tema de la conversión de un site de latin1 (iso-8859-1) a utf-8. Porque la verdad, he tenido bastantes problemas.

    De hecho, a raíz de uno de estos problemas tuve que modificar y ampliar este artículo ;)

    Un saludo!
    SERGI

    ReplyDelete
  3. Hola Sergi, a ver si con tu experiencia me sacas esta duda/pesadilla te planteo la cuestión.
    Tengo todo en UTF8 (docs, webs y mysql) buscando la compatibilidad total claro está. Todo funciona pero si accedes por MySQL se muestran todas las tildes y caracteres especiales con una à raruna. El problema de esto es que si quiero modificar algún registro a mano no puedo sin usar ese símbolo. Si exporto a un excel para un cliente, salen los símbolos y me tiro 10 minutos de remplazos... ¿es así o existe solución?
    Si utf8 nos obliga a usar utf8_encode y decode cada vez que sacamos o metemos en la BDD, sale mas a cuenta latin1 y htmlentities sólo de salida de BDD ¿no?
    Gracias!

    Juanjo

    ReplyDelete
  4. Hola Juanjo, primero de todo te diré que antes de conseguir lo que tú estás persiguiendo (trabajar PERFECTAMENTE SIN PROBLEMAS con todo en utf-8) tuve que batallar mucho, porqué se me escapaban mil detalles. Es por eso que creí de interés escribir este artículo ;)

    Me extraña que si has hecho todo lo que he explicado en el artículo siga sin salirte. Por lo que me cuentas me recuerda mucho a cuando yo no sabía todavía que debía explicitar este comando de PHP-MYSQl para conectar con la base de datos:

    mysql_set_charset('utf8');

    sin este comando... todo lo demás suele fallar!!! Lo curioso es que me costó descubrirlo porqué como según qué configuraciones de servidor uno tenga se ve que no falla, y hay muchos manuales por ahí que no lo mencionan. Pero por lo que tú cuentas, podría ser ese tu problema.

    Una pregunta, cuando dices:

    "Todo funciona pero si accedes por MySQL se muestran todas las tildes y caracteres especiales con una à raruna. "

    ¿a qué te refieres? es decir, ¿como ACCEDES a MySQL? por SSH? por PHP?

    Un saludo! ah. y si has conseguido resolver el misterio y no es nada de lo que yo hubiera sugerido/mencionado en mi artículo, te ruego que lo comentes aquí para que edite el artículo y lo añada!

    SERGI

    ReplyDelete
  5. Hola de nuevo Sergi,
    Gracias por tu respuesta. Me refiero a entrar a mysql por phpmyadmin.ç

    Por ejemplo inserto a la base de datos desde la web "Cañón", lo muestro por la web lanzando una consulta y me sale "Cañón" todo perfecto.

    Entro por phpmyadmin y pone CaÃ~ún (o algo parecido), entonces el problema lo tengo únicamente cuando tengo que cambiar cosas por phpmyadmin que como ponga cañón por la web saldrá el símbolo de un interrogante sobre un rombo.

    Voy a probar mysql_set_charset('utf8'); pero no tengo esperanzas!

    Juanjo

    ReplyDelete
  6. Ha funcionado!!!
    eso si, la función mysql_set_charset('utf8'); la ponen como desaconsejada, en su lugar:
    mysql_query("SET NAMES 'utf8'");
    mysql_query("SET CHARACTER SET utf8");
    mysql_query("SET COLLATION_CONNECTION = 'utf8_unicode_ci'");

    Más complicado pero también funciona! Ahora toca cambiar de todas las bases de datos lo simbolitos raros por las tildes correctas...

    Muchas gracias Sergi

    ReplyDelete
  7. Gracias por compartir con nosotros tus scripts!

    ReplyDelete