Problemas con Charset y PHP

Los problemas de charset surgen de dos cuestiones:

  • Estoy abriendo una fuente utf-8 y mi salida se ve en iso-8859-1
  • o bien el opuesto: iso-8859-1 la fuente, salida utf-8

Vamos a “simular” que abrimos un archivo XML y tenemos problemas con el charset.

Primer caso

Diagnóstico: Por lo general el primer caso se da cuando veo en vez de eñes y acentos dos caracteres. Suelen ser un à y algo más. En el ejemplo el archivo XML estaría codificado en utf-8 (caso más común) y nuestra página se ve en iso-8859-1

Solución: La fuente está viniendo en utf8 y necesito pasarlo a Latin-1. Esto se hace con la función utf8_decode().

$origen = "http://algunblog/feed/";

//CURL mediante o file_get_contents
//En este caso hacemos la conversion de utf-8
//a iso-8859-1
$contenido = utf8_decode(file_get_contents($origen));

$xml = simplexml_load_string($contenido);
//"masajeamos" el contenido del XML

Segundo caso

Diagnóstico: En vez de caracteres especiales vemos unos signos de interrogación o cuadraditos con números. Esto significa que no puede traducir el caracter a alguno Unicode porque está “mal formado”.

Solución: Hay que convertir nuestra cadena a utf-8 mediante la función utf8_encode().

$origen = "http://algunblog/feed/";

//CURL mediante o file_get_contents
//En este caso hacemos la conversion de iso-8859-1
//a utf-8
$contenido = utf8_encode(file_get_contents($origen));

$xml = simplexml_load_string($contenido);
//"masajeamos" el contenido del XML

Estos son los dos casos básicos. Desde ya que esto tiene una explicación un poco más teórica, pero que vamos a abordar en otro post o tal vez le dediquemos una página. Por ahora con esto ya podemos solucionar muchos problemas.

Cuando trabajemos con bases de datos también tenemos que tener en cuenta estas consideraciones. Tenemos ventajas además, por lo general nos permite seleccionar el encoding de la conexión. El parámetro suele llamarse “charset”.

Mi intención es fundar la SALPICADA (Sociedad Argentina por la Lucha contra Problemas Indescifrables de Charset Accediendo a Datos y Archivos) para investigar el tema, capacitar y ofrecer ayuda. Evitemos que nuestras páginas terminen SALPICADAs por feos caracteres que hacen los textos menos legibles.

Cualquier duda, acá estoy.

Etiquetas: , ,

9 comentarios to “Problemas con Charset y PHP”

  1. Alex Says:

    Hola Miguel,

    Me gustaría fervientemente unirme a SALPICADA, para ello y como acto de iniciación planteo un problema que para mi se está convirtiendo en un autentico tormento:

    Mi intención, es mantener un sitio web únicamente bajo codificacion UTF-16LE (¿por qué? … muy largo de expllicar). Para ello utilizo la extension “Multibyte String” de PHP y aplico la siguiente configuracion, en mi php.ini:

    ;;establezco el charset por defecto
    default_charset = UTF-16LE

    ;;no especifico idioma
    mbstring.language = neutral

    ;;establezco la codificacion interna de cadenas en UTF-16LE
    ;;esto me obliga a codificar mis ficheros en UTF-16LE
    mbstring.internal_encoding = UTF-16LE

    ;;anulo el buffer ya que mis ficheros estarán codificados ya en UTF-16LE
    output_buffering = Off
    ;output_handler = mb_output_handler

    ;;habilitamos la traduccion de los datos de entrada a UTF-16LE
    mbstring.encoding_translation = auto
    mbstring.http_input = UTF-16LE

    ;; la siguiente línea no sería necesaria ya que todo está en UTF-16LE
    ; mbstring.http_output = UTF-16LE
    mbstring.substitute_character = none

    Aplicada esta configuración sería esperable que no hubiese problemas y las cadenas de caracteres que defina dentro de mis ficheros se codifiquen ya en UTF-16LE… Eso sería lo deseable, pero me encuentro que si creo un script (codificado el fichero en UTF-16LE) en el que simplemente pongo:

    $dato=”Qué tal la?”;
    echo iconv(“UTF-8″,”UTF-16LE”,mb_detect_encoding($dato));

    Esto me devuelve que $dato está codificado en UTF-8.
    Apreciese que la salida de la función ya tengo que convertirla de UTF-8 a UTF-16LE para que se adapte a este juego de caracteres (definido en cabecera HTTP) y pueda ver el resultado.

    Evidentemente me está haciendo poco caso cuando le digo que el sistema de codificacion interna sea UTF-16LE (que principalmente es lo que buscaba)

    He probado con multiples configuraciones del php.ini y no consigo nada, así que espero que a alguien le pique la curiosidad o se haya enfrentado anteriormente a un problema. Cualquier tipo de luz sobre el tema sería muy de agradecer.

    Un saludo

  2. Miguel Carboni Says:

    Caramba, amigo. Una pavada lo suyo…😉
    Bienvenido a la SALPICADA, somos pocos pero buenos.

    ¿Tuviste que modificar alguna configuración del servidor Web? Por ahí PHP recibe los archivos UTF-8 de Apache. Una manera de probar eso sería corriendo ese ese código desde la línea de comandos con el interprete de PHP y ver que sale.

    Respecto a la configuración, “default_charset” es en qué encoding se va a envíar al navegador, así que mucho en esto no afecta. Parece un poco críptica la documentación disponible. (http://ar.php.net/manual/en/ini.core.php#ini.sect.data-handling)

    Con mb_internal_encoding(); verificás el internal encoding que está usando. Incluso podes setearlo “en caliente” a mano pasándole por parámetro el identificador que podés ver en el array devuelto por mb_list_encodings(). Yo empezaría a probar por ahí.

    Por lo que leí de estos módulos, están orientados al tratamiento de las entradas y la salidas, más que a cómo PHP maneja sus tipos de datos internos.

    No creo que el módulo altere la tipo de dato string de PHP, te da funciones para manipular cadenas con caracteres multibyte, pero los strings definidos desde el lenguaje deben ser en utf-8. Así lo que se maneja como una cadena hardcodeada será manipulada en el charset con que trabaja PHP. Distinto si recibe una cadena no hardcodeada. Por ahí tendrías que probar con eso. Abrir un archivo en ese encoding desde php e imprimir el contenido y probar en que encoding viene eso.

    Me parece que yo también me vuelto críptico. Cualquier cosa te aviso.

    Saludos.

  3. Alex Says:

    Hola Miguel,

    Gracias por tu respuesta.

    Supuse que la directiva mbstring.internal_encoding serviría para codificar aquellas cadenas que defino en el script en ese charset. La verdad es que me guié por el primer comentario que hay posteado en el manual de la funcion mb_internal_encoding (http://es2.php.net/manual/en/function.mb-internal-encoding.php):

    “Be aware that the strings in your source files must match the encoding you specify by mb_internal_encoding. It appears the Parser loads raw bytes from the file and refers to its internal encoding to determine their actual encoding”

    Es decir, pensé que tal y como estaba codificado en el script pasaría a memoria, parece ser que no. Puede que este comentario solo se refiera al código HTML, que si no se utiliza buffer pase directamente a la salida.

    Otro problema que he encontrado es que A VECES ( cuando creo un archivo desde cero no pasa, si empiezo a convertir el fichero de un mapa a otro sí ) pese a que configuro todo para UTF-16LE y el script también está guardado del mismo modo ( con Byte of Mark o sin él ), el interprete PHP no es capaz de leerlo y el apache lo escupe todo para adelante, etiquetas incluidas. Imagino que es un error de codificación del archivo pero lo abro con distintos editores y en todos me detecta bien la codificación (extraño, quizá algún caracterer mal convertido que los editores lo ignoran y el PHP no traga).

    En fin es un tema bastante oscuro y sobre el que no hay mucha información, por lo que se agracede sitios donde poder dejar cada uno su aportación y aprender de los demás.

    Un saludo y muchas gracias

  4. Miguel Carboni Says:

    Alex:

    Vaya que es oscuro el tema. El drama nace de que PHP no tiene soporte nativo a cadenas multi-byte, pero sí las puede tratar.

    Al parecer vas a tener que esperar a PHP 6 que sí incluiría soporte interno.
    Voy a seguir buscando del tema y si encuentro algo lo publico.

    Muchas gracias a vos por tu interés y por usar este espacio que para eso lo pensé. Para compartir experiencias y conocimientos.

    Saludos.
    Miguel

  5. Gabriel Capote Says:

    Saludos,

    Muchas gracias por la información, ha sido de gran ayuda para el proyecto que estoy desarrollando actualmente: reumatologiapediatrica.com.ve

    El problema que hemos solucionado es específicamente la inconsistencia entre el charset de la base de datos y el de prototype (Librería que implementa AJAX), más concretamente su función serialize, utilizada para el pasaje de los parámetros de la vista al controlador, que causaba la aparición de caracteres extraños en lugar de los acentos y eñes.

    Atte. Gabriel Capote

  6. Luis Alberto Says:

    Muchas gracias por la informacion me sirvio mucho esa funcion, por que en mi sitio salia el caracter correcto pero en la base de datos estaba ese A 3
    con la funcion que pusiste utf8_decode asunto solucionado muchas gracias!

  7. Miguel Says:

    Siempre existe la posibilidad de setear la charset de la conexión mediante la función

    mysql_set_charset($encoding_con_el_sale_mi_pagina);
    

    Eso siempre y cuando no choque con otros.

    Mucha suerte!

  8. Jesus Lopez Says:

    Muchas gracias, me has ayudado mucho!

  9. Cómo solucionar el problema con caracteres extraños en acentos y eñes: charset UTF-8 / ISO-8859-1 | GeeksRoom Says:

    […] [fuente: LodeMiguel] […]

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s


A %d blogueros les gusta esto: