La solución a los problemas con los acentos en PHP, MySQL y HTML

Somos muchos los que, cuando estamos comenzando a programar, nos encontramos con un problema que a veces nos consume mucho mas tiempo del que debería y a veces al buscar una solución en la red lo complicamos todavía más, debido a la gran cantidad de información existente y a que en muchas de las webs y blogs que he estado viendo te lian mas que ayudan.

Yo voy a explicar mi método, sin entrar a ver las diferencias entre ISO 8859-1 y UTF-8, que son las dos mas comunes.

En mi caso yo siempre uso UTF-8 y la razón es que en las aplicaciones que estoy desarrollando utilizo mucho la clase SimpleXML de PHP y todos los datos extraidos con esta clase siempre están codificados en UTF-8, si mi aplicación utilizara ISO-8859-1 tendría que pasar toda la información por la función utf8_decode() de PHP, con lo que estaría sobrecargando el script sin necesidad.

La clave está en que hay que "decirle a todo" que cotejamiento es el que estamos utilizando.

1.- Codificación del documento.

En primer lugar hay que comprobar la codificación del documento, si usas DW y Windows en español posiblemente el valor de Codificación por defecto lo tendrás en Europeo occidental.

En Dreamweaver lo puedes comprobar pulsando CRTL-J, o en modificar - propiedades de pagina.

En Notepad++ lo verás en la pestaña Codificación.
Codificacion del archivo almacenado en PC

Si esto lo tienes bien, tu etiqueta <meta> del documento debería ser como sigue:

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

2.- MySQL

Aquí es donde surgen la mayor parte de los problemas. Cuando creamos las tablas en la base de datos tenemos que tener especial cuidado de que todos los cotejamientos estén en utf-8. Cuando lo dejas por defecto, si utilizas phpmyadmin este proceso se convierte en una lotería y cuando termines de crear tu base de datos verás que cada cosa está cotejada de forma diferente...

Yo voy a utilizar UTF-8, si te decantas por ISO-8859-1, en todas las selecciones que nombro a continuación tendrás que elegir latin1_general_ci, si eliges latin1_spanish_ci también funcionará, pero por favor, usa en todas lo mismo.

Cotejamiento para las conexiones al servidor.

Cotejamiento de conexiones al servidor.

Cotejamiento para la base de datos

En la pestaña operaciones de la pantalla principal de la base de datos

Cotejamiento Base Datos

Cotejamiento para las tablas.

Cada tabla que creemos deberá tener el mismo cotejamiento

Cotejamiento de Tabla

Cotejamiento de Campo o Columna.

Es importante indicar el cotejamiento que va a tener el Campo, sobre todo si en este vamos a almacenar caracteres de texto. Lógicamente a un campo numérico, booleano etc no hay que indicar el cotejamiento porque su contenido no incluirán caracteres "extraños".

Cotejamiento de Campo

3.- PHP

Por último vamos a "decirle" al Mysql en PHP cómo nos vamos a comunicar con la base de datos para que no haya ningún tipo de confusión.

Yo utilizo siempre PDO para la conexion de PHP con la base de datos, pero esto es otro tema que da para mucho. Así quedaría la conexión. Al crear el manejador de la conexión hay que incluir en el array de opciones la codificación, como sigue:

$pdo = new PDO('mysql:host= servidor; dbname=bd', $usuario, $clave, array(PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES  \'UTF8\''))

Si usas la conexion estandar, basta con añadir

mysql_set_charset('utf8');

justo despues de abrir la conexion con la base de datos.

104 comentarios en “La solución a los problemas con los acentos en PHP, MySQL y HTML

    1. Alberto

      Eres la máquina!! Después de toda la mañana y parte de la tarde como loco con este tema, me has dado la solución más sencilla. Gracias!!

      Responder
    1. dbarreno

      Hola Juan Carlos,
      Gracias por tus comentarios.
      En MySQL hay varias formas de almacenar una fecha. Si no necesitas almacenar la hora yo te recomiendo usar el tipo de datos DATE.
      El formato para almacenar la fecha sólo puede ser YYYY-MM-DD, esto no se puede cambiar, pero eso no tiene que suponer ningún problema ya que existen muchas formas de "formatear" la fecha para mostrarla como nosotros queramos.
      Yo personalmente prefiero formatearla en PHP utilizando la clase DateTime() de la siguiente forma:

      $Fec = new DateTime('2013-11-25');
      $fecha = $Fec->format("d-m-Y");

      Otra forma es usar la típica funcion date() de esta forma
      $fecha = date('d-m-Y', strtotime('2013-11-25'))
      Pero ojo con este método ya que no soporta fechas inferiores al 13-Diciembre-1901 ni (lo que es mas importante) superiores a 19-Enero-2038.
      Pero hay otra forma y es que MySQL te de ya la fecha en el formato que necesites. Para ello hay que utilizar las funciones que MySQL tiene para utilizar con las fechas.
      La forma de usarlas es incluirlas en las consultas y un ejemplo podría ser el siguiente:
      SELECT `usuarios`.`nombre`, DATE_FORMAT(`usuarios`.`fecha_nacimiento`, '%d-%m-%Y') AS fecha_nacimiento FROM `usuarios` WHERE `usuarios`.`id_usuario` = 1

      Huye de almacenar fechas como VARCHAR, es poco eficaz y sólo te traerá problemas.
      Espero haberte ayudado.
      Un saludo

      Responder
  1. alopez

    Muy buen articulo, llevo mucho tiempo desarrollando y nunca me había ocurrido el problema... solo ahora que decidí pasarme a un servidor mas grande .... me ocurrió esto ...

    Responder
  2. SrlLvrs

    Por fin!! Gracias amigo, te agradezco esto, me dí muchos cabezazos antes de darme cuenta de mi error -.-"

    Algo que tal vez puedas agregar, es que si están trabajando con Sublime Text 2, el archivo .PHP van a menú File -> Save with Encoding -> UTF-8 with BOM.

    Saludos!! 😀

    Responder
  3. Martín

    Anduvo espectacular tu solución, resumiendo:
    En HTML:

    En MYSQL:
    En la base, tabla y datos colocar en el cotejamiento utf8_general_ci
    En PHP:
    Luego de conectar con la base de datos
    mysql_set_charset('utf8');
    Saludos

    Responder
  4. henry h bogota, colombia

    Con phpmysqladmin necesito crear tabla con filas así: CUENTA y los doce meses, cada uno con SALDO ANTERIOR, DEBITOS en ese mes, CREDITOS en ese mes y NUEVO SALDO o sea un array para 12 meses dentro de una columna o tendré que definir 4 columnas por mes y definir entonces 4X12=48 columnas? Estoy aprendiendo, MUCHAS GRACIAS por orientarme!.

    Responder
  5. Andres Diaz (AnzOne)

    Muchas gracias, mi problema se debía al cotejamiento en la DB, entidades y campos; a través de sugerencias probe con latin, utf-spanish_ci, ci2 y no funciono; otra alternativa es con htmlentities.
    La configuración del editor en mi caso Geany es en: Documento->EstablecerCodificación->Unicode->Unicode(UTF-8)

    Responder
  6. E-RZ

    Hola les comento que intente esto y no me funciono, mi principal problema es que parece que el comando mysql_set_charset(); ya no funciona, después de una búsqueda lo cambie por:

    mysqli_set_charset(database, 'utf-8');

    y al parecer funciona perfectamente.

    Saludos

    Responder
    1. dbarreno

      Buenas,

      Gracias por el aporte.
      MySQLi es una extensión algo más moderna de MySQL, mas recomendable de usar que ésta última, ya que efectivamente está obsoleta a partir de la version 5.5 de PHP y tiende a desaparecer.
      Pero actualmente sigue operativa, el motivo de que no te funcione mysql_set_charset() es que en tu script abrirás la conexión con mysqli_connect(), lo que significas que te conectas a la base de datos utilizando esta extensión y por tanto todas las funciones que utilices deben pertenecer al grupo de funciones de esta extension: mysqli_error(), mysqli_fetch_array() etc.

      De todas formas yo lo que recomiendo es utilizar PDO, es mas rápido y mas seguro, y no es tan complicado como al principio puede parecer. Con unos conocimientos mínimos de programacion orientada a objetos se puede implementar sin dificultad.

      Un saludo

      Responder
  7. CARLOS

    ESTOY UTILIZANDO LA CLASE FPDF Y SIGO LAS INSTRUCCIONES ANTES EXPUESTA Y NO CONSIGO MOSTRAR LAS Ñ Y TILDES. QUE DEBO HACER POR FAVOR AYUDA

    Responder
    1. dbarreno

      Hola,
      Con tan poca información es muy complicado poder ayudarte. En la documentación de FPDF podrás encontrar el juego de caracteres que utiliza la clase.
      Dependiendo de qué estés utilizando tu y cómo esté configurado la clase externa (fpdf), intenta aplicar a cada cadena de texto la funcion utf8_encode() o utf8_decode(), a ver que tal.
      No es la mejor forma, pero a veces no queda mas remedio. Lo ideal sería configurar la clase FPDF para que toda la aplicación utilice el mismo juego de caracteres, pero con tan poca información, es lo que puedo decirte
      Saludos

      Responder
  8. Reynaldo

    Gracias amigo me ha sido de ayudo.
    Amigo tengo un problema y no se como solucionarlo, lo que pasa es cuando yo creo un documento HTML en cualquier programa (sublime text, notepad, dreamweaver, netbean) corren con normalidad pero cuando creo un documento PHP en cualquiera de los mencionados a excepción del -NetBeans- no se muestran nada por ejemplo este codigo:

    No se muestra. Esoty utilizando el xampp y le tengo el apache y mysql activado.

    Responder
  9. Yorch

    Te quieeerooooo. Hace meses y meses, por no decir años, que tengo el problemilla de las tildes, y hasta ahora hacía apaños sustituyendo caracteres, pero finalmente y GRACIAS A TI se me ha resuelto. ¡Qué fácil parecía! Y pensar que tenía las tablas en un cotejamiento y las webs en otro... ¡¡GRACIAS!!

    Responder
  10. eljihe

    estoy utilizando fpdf pero los datos que se extraen no reconoce los acentos
    coloque en la conexion mysql_set_charset('utf8'); y en el multicell utf8_decode pero aun así nada

    Responder
  11. Pingback: Problemas de Acentos y caracteres al traer datos de un Database con PHP | Informatic To You

  12. Jesús Piña

    Muy buen artículo una pregunta ¿Que diferencia hay entre lo que escribes con la vieja técnica de cambiar los acentos por su equivalente en HTML por ejemplo José Peña por José Peña?

    Responder
    1. dbarreno

      Hola Jesús,
      Con eso de la vieja técnica supongo que te refieres a usar entidades del tipo &oacute ;.
      No suelo usar entidades por varios motivos (si las usas que sea dentro del código HTML). Estando todo bien configurado no es necesario.
      No recomiendo almacenar nunca entidades en base de datos, asi que cuidado con los editores como TinyMce.

      Te pongo un ejemplo sencillo de por qué no lo recomiendo (puedes hacer pruebas de lo que te comento en tu phpMyAdmin local). Si quieres, por ejemplo, montar en tu web tu propio buscador interno, a la hora de buscar un usuario va a escribir, por ejemplo, Africa con acento o sin acento.
      Si tienes almacenado África y una configuración correcta de caracteres al realizar tus querys ejemplo: ....LIKE 'africa'... te va a dar resultados tanto si el usuario escribió África con tilde o sin ella.
      Pero si tienes almacenado en tu tabla &Aacute ;frica ni Africa, ni África te van a dar resultados, al menos que incluyas una función que te convierta antes en entidad la letra acentuada (si el cliente ha puesto la tilde). Esto de andar convirtiendo con funciones es tiempo de procesamiento, que hay que evitar para optimizar nuestra web al máximo, y además funciona relativamente bien si estas usando ISO, pero si en cambio usas UTF8 las conversiones se complican, tienes que crearte tus propias funciones.

      Se me ocurren mas motivos, pero creo que este es el mas claro, y además ya me he extendido demasiado xD

      Gracias por tu comentario
      Saludos

      Responder
    1. dbarreno

      Hola,
      Comprueba que los caracteres extraños no estén ya mal almacenados, en cuyo caso la solución es complicada.
      No se a que te refieres con Enters, podrias concretar?

      Gracias por tu comentario.

      Responder
  13. nicolas lopez

    Si señor, despues de ver en varios foros especialistas por fin consegui, se trataba del "set_charset" despues de abrir la conexion, muchisimas gracias por ayudar a los que estamos aprendiendo.

    Responder
  14. yemir0904

    Hola, disculpe, en phpmyadmin que tipo de fecha puedo usar si solo quiero que me indique en el registro un periodo, por ejemplo mes y año [junio - diciembre 2014]..

    Responder
    1. dbarreno

      Hola Yemir,
      Disculpa que no haya respondido antes a tu cuestión. Me había entrado como SPAM y lo acabo de rescatar. Supongo que ya lo tienes solucionado, pero respondo lo que yo haría en este caso, por si acaso alguien tiene un caso similar.
      Si lo que quieres es almacenar un periodo de tiempo yo siempre utilizo 2 registros, uno para la fecha inicio y otro para la fecha fin del periodo y el campo, por supuesto de tipo DATE. Siempre que quieras trabajar con fechas utiliza DATE. Si obligatoriamente tienes que almacenar la estructura con el formato Junio - Diciembre 2014 tendras que usar un campo string, pero no podrás realizar ningún tipo de operación de fecha con ese registro. Simplemente será una cadena de texto sin mas.
      En cambio, utilizando fecha inicio y fecha fin tienes muchas posibilidades para realizar ordenación y busquedas correctamente.
      Si a alguién se le ocurre otra forma, se agradecen los aportes.
      Saludos.

      Responder
  15. angel

    @dbarreno grande igual que a muchos otros me has salvado de una mala noche jaja directo a favoritos, por favor sigue actualizando el blog 😉

    Responder
  16. Cesar Martin Esparza Vera

    Muchisisisísimas gracias!!!!! Con este post pude solucionar el problema que tenía con los acentos y ñ's, al hacer mis consultas......!!!! Gracias por todo......... ERES GRANDE.. 😀

    Responder
  17. Pingback: Los problemas de acentos con NetBeans | Viajes y programacion

  18. Pablo Aragón

    Buenos días, he hecho todo lo mencionado para el caso de phpmyadmin y cuando exporto una tabla como CSV para excel, el archivo que me arroja pone donde hay tildes a ñ simbolos raros.

    Responder
    1. dbarreno

      Buenas,
      No tiene nada que ver la codificación de caracteres con el reconocimiento de mayusculas, minusculas.
      Eso es otro parámetro, tendrías que elegir la opción correcta.

      Responder

Deja un comentario

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