Problemas codificacion web. Configurar codificación utf8 php, mysql y html
Después de muchos años y grandes problemas de codificación con php y mysql, puedo afirmar que tengo, bastante controlado el problema con las codificaciones. Bueno por lo menos identificado. Desgraciadamente mi conocimiento se debe a malas experiencias, pero como se dice, casi todo en la vida se aprende a golpes o por las malas. Me he tenido que “pelear” muchísimo con este problema y han sido grandes quebraderos de cabeza. Aún así se que es uno de los grandes problemas de los desarrolladores y se convierte en algo tremendamente problemático sino se hace bien desde el principio. Es el momento de escribir un artículo con mis experiencias sobre este asunto. Problemas codificación web. Configurar codificación utf8 php, mysql y html
Hay muchas causas por las que una página web muestre caracteres raros con problemas de codificación en iso o con utf8. Un listado de los posibles fallos pueden ser los siguientes:
- Codificación por defecto con la que arranca apache. Hay que indicar una directiva para que apache arranque en utf8
- Codificación con mysql. Esto es lo más complejo de todo este asunto. Lo explicaré después.
- Codificación de php.
- Codificación del html.
- Por último y no menos importante la codificación con la que se crean y guardan los archivos.
Las codificaciones más frecuentes son iso-8859-1 y utf-8. El problema que se generen caracteres raros reside en la mezcla y utilización de ambas codificaciones al mismo tiempo. Podemos usar iso-8859-1 para nuestra web pero todos los puntos que he comentado anteriormente deben estar en iso. No puede haber mezcla de algunas partes en iso y otras en utf8. Si usamos iso podemos tener una web en español con acentos y eñes funcionando. Incluso en portugués también funcionaría el carácter ç. Pero no funcionaria si queremos hacer una web con alfabeto chino, japonés o griego.
Sin duda lo más recomendable y lo que se hace es usar utf8. Todo en utf8.
A continuación indico el tutorial que habría que seguir para solucionar los problemas de codificación.
En un servidor en producción habría que ejecutar todos los cambios al mismo, o plantear una estrategia de migración. No se puede cambiar el contenido de las tabla a utf8 y que mysql siga en iso, o no se puede cambiar la codificación de los archivos y que sigan las cabeceras en otra codificación diferente. Bueno en realidad si se puede, pero no creo que sea muy bueno para un usuario que te esta visitando presenciar "el festival" de codificaciones y errores. Primero hay que localizar el problema y si se considera que tan solo hay que cambiar un punto y que no supone mucho tiempo no habría problema, pero si son varios temas los que hay que modificar sería importante establecer un protocolo de migración. Sobre todo si el problema viene de mysql.
Configurar la codificación del servidor apache con utf8
Apache es una herramienta con la que montamos nuestro servidor de páginas web. Una de las piezas de nuestro puzzle. Puede afectar a nuestra codificación y su archivo de configuración contiene una directiva para especificar la codificación con la que arranca y que usa el servicio. Esta directiva es AddDefaultCharset .
Editamos el archivo de configuración de apache
1
vi /etc/httpd/conf/httpd.conf
Y verificamos que existe y está bien establecida la siguiente directiva de configuración para la codificación:
1
AddDefaultCharset UTF-8
si hemos tenido que cambiar esta directiva, habrá que guardar el fichero y salir
1
2
:wq
Después reiniciamos el servicio de apache
1
2
3
# /etc/init.d/httpd restart
Parando httpd: [ OK ]
Iniciando httpd: [ OK ]
Configurar la codificación de php en utf8
PHP siempre devuelve una codificación de caracteres por defecto, la cual está definida con la cabecera Content-type. Siempre hay que especificar del lado servidor el encabezado y la codificacion. Para ello hay que utilizar la función de header() antes de generar cualquier tipo de contenido, por ejemplo:
1
header ('Content-type: text/html; charset=utf-8');
Lo más común es forzar el charset a utf-8 con la función header() desde nuestro código, pero siempre se puede especificar la codificación de la cabecera editando la configuración del php.ini
1
vi /etc/php.ini
encontraremos el siguiente párrafo. Tendremos que descomentar la última linea que se indica.
1
2
3
4
5
6
7
; As of 4.0b4, PHP always outputs a character encoding by default in
; the Content-type: header. To disable sending of the charset, simply
; set it to be empty.
;
; PHP's built-in default is text/html
default_mimetype = "text/html"
;default_charset = "utf-8"
Ajustando la codificación de Apache y php, ya tendríamos la configuración del lado del servidor preparada.
Con un sniffer o un proxy comprobaríamos las cabeceras para ver la codificación que devuelve. Tiene que ser algo parecido a esto:
Configurar la codificación html en utf8
Siempre se debe especificar la codificación utilizada para una página HTML o XML. De lo contrario, nos arriesgamos a que los caracteres del contenido aparezcan mal. Esto sería un problema para los usuarios de la web, el humano tendría problemas para leer o distinguir lo que pone, pero también sería un problema de interpretación para las máquinas, los crawlers, robots y arañas que analizan el contenido de nuestra web.
Dentro de la etiqueta head
1
<head> </head>
habría que incluir la siguiente etiqueta con la codificación del documento html.
En HTML5 se especifica la etiqueta
1
<meta charset="UTF-8">
En HTML 4 se usa la etiqueta
1
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
Para un documento XML se utiliza la siguiente etiqueta
1
<?xml version="1.0" encoding="UTF-8"?>
Crear y guardar los archivos en utf8
También hay que tener en cuenta como se guardan los ficheros que se van creando. No olvidar que los ficheros van a ser ejecutados por php bajo un servidor apache en linux (por lo general). Si se trabaja en windows el IDE usará la codificación del sistema operativo por defecto, iso. Por lo general esto es algo que se puede configurar en cualquier IDE o editor de código. Hay que tener cuidado con esto y crear una pauta para guardar todos los fichero en utf-8 y si existe la opción, especificar para que sistema operativo o que formato de fichero guardar.
Una diferencia fundamental en los archivos de texto es que UNIX utiliza un sólo salto de línea (LF) o Simple Line Feed para indicar una nueva línea, Windows utiliza un retorno de carro o Line Feed (CRLF). Tan sólo esta diferencia puede hacer que se produzca un error y tardemos muchísimo tiempo en darnos cuenta.
Cuando tenemos problemas de codificación con el propio archivo hay que usar varias herramientas en linux.
El comando dos2unix para convertir los formatos de fichero dos a unix-linux
O utilizar el comando iconv para generar nuevos ficheros en utf8.
1
iconv -f iso-8859-1 -t utf-8 archivo.php > archivo_utf8.php
Nota: no siempre me ha funcionado esta última técnica.
Configurando el servidor mysql con utf8
Lo mejor para el final! Es lo que da más problemas y lo más difícil de arreglar. Dentro de la codificación de mysql hay otros puntos que hay que seguir para arreglar los problemas que haya con la codificación. Ya que mysql cuando conecta lo hace con un tipo de codificación, cuando lee lo hace con un tipo de codificación y cuando guarda lo hace con otro tipo de codificación! Todos tienen que estar en utf-8!
El primer paso es ejecutar la siguiente sql.
1
show variables like '%character%';
Si devuelve el siguiente resultado, es que por lo menos la configuración de mysql en el archivo /etc/my.cnf esta ok
| character_set_client | utf8 | | character_set_connection | utf8 | | character_set_database | utf8 | | character_set_filesystem | binary | | character_set_results | utf8 | | character_set_server | utf8 | | character_set_system | utf8 | | character_sets_dir | /usr/share/mysql/charsets/ | | collation_connection | utf8_general_ci | | collation_database | utf8_general_ci | | collation_server | utf8_general_ci |
Si no esta todo en utf-8 tendremos que editar el archivo de configuración de mysql /etc/my.cnf agregando o editando las siguientes directivas dentro de la etiqueta mysqld.
1
2
3
4
5
6
[mysqld]
init_connect='SET NAMES utf8'
default-character-set=utf8
character-set-server=utf8
collation-server=utf8_general_ci
skip-character-set-client-handshake
Una vez realizados los cambios reiniciamos el servicio de mysql
1
/etc/init.d/mysqld restart
- init_connect='SET NAMES utf8': Codificación inicial de la conexión de mysql. Define la codificación del cliente, de la conexión y de los resultados de lectura.
- default-character-set=utf8: Codificación por defecto de la interacción con el usuario.
- character-set-server=utf8: Juego de caracteres o character ser por defecto del servidor. La directiva character_set_database toma el valor por defecto que tenga definido esta directiva.
- collation-server=utf8_general_ci: Estable el collation por defecto, que por ejemplo usan las tablas y los campos de éstas.
- skip-character-set-client-handshake: Ignora la codificación de conexión del cliente, estableciendo la codificación de conexión del servidor. Es un comportamienteo de MySQL 4.
La directiva SET NAMES utf8 equivale a las siguientes 3 directivas:
1
2
3
SET character_set_client = x;
SET character_set_results = x;
SET character_set_connection = x;
Como se puede ver hay muchas directivas de codificación en la configuración de mysql. Incluso es posible que lo que yo he escrito y me ha funcionado sirva para mucha gente pero a lo mejor hay quien no le sirva y tenga que seguir ajustando directivas. No es algo sencillo dejar funcionando la codificación de mysql a la perfección en el primer intento. En los links que hay al final del artículo incluyo unas lecturas recomendadas entre ellas, la página con todas las directivas del archivo de configuración de mysql my.cnf.
Nota: ejecutar la sql
1
mysql_query("SET NAMES 'utf8'");
al crear la conexión no esta mal si el resto de la configuración está bien, sino es un parche, que no esta solucionando el problema de raíz que es la configuración de mysql.
Pues bien hasta este punto tendríamos toda nuestra aplicación funcionando a la perfección en utf8.
Migrar toda la base de datos de latin1 o iso a utf8
Pero claro en base de datos habrá muchos problemas de codificación con registros ya creados. Ahora toca migrar todo el contenido de latin o iso a utf8. Los procedimientos pueden ser varios. Pero lo más recomendable aunque más laborioso es lo siguiente:
-
- Parar el servicio de mysql. En caso de que no hayamos hecho los cambios y reiniciado, porque sino haremos todas las modificaciones pero se seguirán generando registros mal codificados.
- Hacer un mysqldump para exportar toda la base de datos existente.
- Si el dump de la base de datos no es muy grande podemos editarlo con vi o nano. Habrá que buscar todos los create table y especificar el charset o modificarlo en consecuencia por utf8.
1
2
3
4
5
CREATE TABLE `TABLA` (
&<%%KEEPWHITESPACE%%&> `id` mediumint(8) unsigned NOT NULL auto_increment,
&<%%KEEPWHITESPACE%%&> // resto de campos
&<%%KEEPWHITESPACE%%&> PRIMARY KEY (`id`)
&<%%KEEPWHITESPACE%%&> ) ENGINE=MyISAM DEFAULT CHARSET=utf8
-
- Si el collation de cada campo de una tabla esta con iso o latin tambien habrá que cambiarlo por utf8
- Buscar la sql que crea la base de datos y cambiar o modificar el character set y el collate a utf8
1
2
3
CREATE DATABASE MI_BASE_DATOS
&<%%KEEPWHITESPACE%%&> DEFAULT CHARACTER SET utf8
&<%%KEEPWHITESPACE%%&> DEFAULT COLLATE utf8_general_ci;
-
- Ahora hay que buscar y reemplazar cada uno de los carácteres que están mal codificados por su correspondiente vocal con acento o letra. Para reemplazar desde el vi utilizar el siguiente comando
1
2
3
4
5
6
7
8
9
10
# reemplaza el caracter á por á
:%s/á/á/g
# así con cada una de las vocales
:%s/í©/é/g
:%s/Ã/í/g
:%s/ó/ó/g
:%s/íº/ú/g
:%s/ú/ú/g
:%s/ñ/ñ/g
:%s/í‘/Ñ/g
- Una vez que todo está en utf8 y todo el texto y contenido de las base de datos esta bien formateado y codificado. Borramos la base antigua o la reemplazamos por el mysqldump que acabamos de editar.
- Arrancamos mysql y listo
Si en las tablas siguen quedando carácteres residuales, se pueden lanzar sqls para remplazar automáticamente.
Sqls para modificar los carácteres mal guardados.
1
2
3
4
5
6
7
8
update TABLA set CAMPO_REEMPLAZANDO = replace(CAMPO_REEMPLAZANDO, 'á', 'á');
update TABLA set CAMPO_REEMPLAZANDO = replace(CAMPO_REEMPLAZANDO, 'í©', 'é');
update TABLA set CAMPO_REEMPLAZANDO = replace(CAMPO_REEMPLAZANDO, 'Ã', 'í');
update TABLA set CAMPO_REEMPLAZANDO = replace(CAMPO_REEMPLAZANDO, 'ó', 'ó');
update TABLA set CAMPO_REEMPLAZANDO = replace(CAMPO_REEMPLAZANDO, 'íº', 'ú');
update TABLA set CAMPO_REEMPLAZANDO = replace(CAMPO_REEMPLAZANDO, 'ú', 'ú');
update TABLA set CAMPO_REEMPLAZANDO = replace(CAMPO_REEMPLAZANDO, 'ñ', 'ñ');
update TABLA set CAMPO_REEMPLAZANDO = replace(CAMPO_REEMPLAZANDO, 'í‘', 'Ñ');
Para reemplazar carácteres con entidades html
1
2
3
4
5
6
update TABLA set CAMPO_REEMPLAZANDO = replace(CAMPO_REEMPLAZANDO, 'amp;aacute;', 'á');
update TABLA set CAMPO_REEMPLAZANDO = replace(CAMPO_REEMPLAZANDO, 'amp;eacute;', 'é');
update TABLA set CAMPO_REEMPLAZANDO = replace(CAMPO_REEMPLAZANDO, 'amp;iacute;', 'í');
update TABLA set CAMPO_REEMPLAZANDO = replace(CAMPO_REEMPLAZANDO, 'amp;oacute;', 'ó');
update TABLA set CAMPO_REEMPLAZANDO = replace(CAMPO_REEMPLAZANDO, 'amp;uacute;', 'ú');
update TABLA set CAMPO_REEMPLAZANDO = replace(CAMPO_REEMPLAZANDO, 'amp;ntilde;', 'ñ');
En este punto ya tendremos también nuestro contenido de la base de datos bien codificado e insertado.
Importante! Una vez que esto esté configurado habría que ir quitando todos los utf8_decode(); y utf8_encode(); de la programación. Si todo está bien configurado ya no se necesita estar codificando y decodificando el texto que vamos guardando en base de datos y mostrando en la web. Adicionalmente ya no se pueden usar las funciones de php para procesar cadenas de texto tales como strtolower(), substr(), etc.. sino que hay que usar las respectivas funciones mb_string() o Multibyte que son las encargadas de tratar los textos en utf-8 que tienen un numero mayor de bytes.
Como conclusión final, hay que añadir que también tener una buena metodología y acostumbrarse a hacer bien las cosas es fundamental, de manera que por ejemplo siempre nos tenemos que fijar como se guardan los ficheros, en que codificación creamos una base de datos, sus tablas y sus campos,etc… Es algo muy sencillo pero requiere ser metódico, cuesta empezar a serlo.
Espero les resulte útil, y no duden en dejar algún comentario con sus dudas, preguntas o inquietudes.
Lecturas recomendadas : http://www.iana.org/assignments/character-sets http://www.w3.org/International/O-HTTP-charset http://www.w3.org/International/techniques/server-setup#characters http://www.w3.org/TR/i18n-html-tech-char/#ri20030112.213746362 http://dev.mysql.com/doc/refman/5.0/en/server-system-variables.html http://dev.mysql.com/doc/refman/5.0/es/charset-connection.html