PHP: convierte la salida curl_exec en UTF8

Me gustaría trabajar solo con UTF8. El problema es que no sé el juego de caracteres de cada página web. ¿Cómo puedo detectarlo y convertirlo a UTF8?

 true, ); curl_setopt_array($ch, $options); $data = curl_exec($ch); // $data = magic($data); print $data; 

Mira esto en: http://paulisageek.com/tmp/curl-utf8

¿Qué es magic() ?

Siguiendo el consejo de Gumbo y Pekka, escribí curl_exec_utf8

 /** The same as curl_exec except tries its best to convert the output to utf8 **/ function curl_exec_utf8($ch) { $data = curl_exec($ch); if (!is_string($data)) return $data; unset($charset); $content_type = curl_getinfo($ch, CURLINFO_CONTENT_TYPE); /* 1: HTTP Content-Type: header */ preg_match( '@([\w/+]+)(;\s*charset=(\S+))[email protected]', $content_type, $matches ); if ( isset( $matches[3] ) ) $charset = $matches[3]; /* 2:  element in the page */ if (!isset($charset)) { preg_match( '@ element in the page */ if (!isset($charset)) { preg_match( '@<\?xml.+encoding="([^\s"]+)@si', $data, $matches ); if ( isset( $matches[1] ) ) { $charset = $matches[1]; /* In case we want do do further processing downstream: */ $data = preg_replace('@(<\?xml.+encoding=")([^\s"]+)@si', '$1utf-8', $data, 1); } } /* 4: PHP's heuristic detection */ if (!isset($charset)) { $encoding = mb_detect_encoding($data); if ($encoding) $charset = $encoding; } /* 5: Default for HTML */ if (!isset($charset)) { if (strstr($content_type, "text/html") === 0) $charset = "ISO 8859-1"; } /* Convert it if it is anything but UTF-8 */ /* You can change "UTF-8" to "UTF-8//IGNORE" to ignore conversion errors and still output something reasonable */ if (isset($charset) && strtoupper($charset) != "UTF-8") $data = iconv($charset, 'UTF-8', $data); return $data; } 

Las expresiones regulares provienen principalmente de http://nadeausoftware.com/articles/2007/06/php_tip_how_get_web_page_content_type

La conversión es fácil. La detección es la parte difícil. Podrías probar mb_detect_encoding pero es un método muy inestable, literalmente es “adivinar” el tipo de contenido y como @troelskn resalta en los comentarios, puede adivinar las diferencias “duras” en el mejor de los casos (¿Es una encoding de varios bytes?) Pero falla al detectar matices de juegos de caracteres similares.

La forma correcta sería IMO:

  • Interpretar cualquier metaetiqueta de content-type en la página
  • Interpretar cualquier encabezado de content-type enviado por el servidor
  • Si eso no produce nada, intente “olfatear” la encoding utilizando mb_detect_encoding ()
  • Si eso no produce nada, vuelva a un valor predeterminado definido (tal vez ISO-8859-1, tal vez UTF-8).

A diferencia de lo descrito en las directrices en la respuesta de @ Gumbo, personalmente creo que las tags Meta deberían tener prioridad sobre las cabeceras del servidor porque estoy bastante seguro de que si hay una etiqueta Meta, ese es un indicador más confiable de la encoding real de la página que una configuración de servidor que algunos operadores de sitios ni siquiera saben cómo cambiar. La forma correcta, sin embargo, parece ser tratar los encabezados de tipo de contenido con mayor prioridad.

Para el primero, creo que puedes usar get_meta_tags () . Lo último que deberías obtener de curl ya, solo tendrías que analizarlo. Aquí hay un ejemplo completo sobre cómo procesar sistemáticamente los encabezados de respuesta servidos por cURL.

La conversión estaría usando iconv :

 $new_content = iconv("incoming-charset", "utf-8", $content); 

Estuve muy feliz de encontrar esta respuesta, pero me di cuenta de que hay un error en la detección de la etiqueta . Simplemente no parecía coincidir con ninguna etiqueta de contenido, y aún no está equipada para las nuevas tags de estilo HTML5: . Así que escribí esto, espero que les ayude, ¡y gracias de nuevo por esta excelente solución!

 /* 2:  element in the page */ if (!isset($charset)) { preg_match('/<[\s]*meta[^>]*charset="?([^\s"]+)\s?"/i', $data, $matches); if (isset($matches[1])) { $charset = $matches[1]; } } 

(PD. No pude encontrar la manera de publicar esto como un comentario, ya que obviamente no es una respuesta completa).

Hay un orden definido de cómo especificar la encoding de caracteres en HTML :

[…] los agentes de usuario conformes deben observar las siguientes prioridades al determinar la encoding de caracteres de un documento (desde la prioridad más alta hasta la más baja):

  1. Un parámetro HTTP “charset” en un campo “Content-Type”.
  2. Una statement META con “http-equiv” establecido en “Content-Type” y un valor establecido para “charset”.
  3. El atributo charset establecido en un elemento que designa un recurso externo.

Si no está presente ninguna statement de encoding de caracteres, HTTP define ISO 8859-1 como encoding de caracteres predeterminada . También puede usar eso como encoding de caracteres predeterminada para HTML o simplemente rechazar procesar la respuesta.

Para XHTML, también tiene la statement XML como fuente para la encoding :

En un documento XML, la encoding de caracteres del documento se especifica en la statement XML (por ejemplo, ). Para presentar documentos de forma portátil con codificaciones de caracteres específicas, el mejor enfoque es garantizar que el servidor web proporcione los encabezados correctos. Si esto no es posible, un documento que desee establecer su encoding de caracteres explícitamente debe incluir tanto la statement XML como una statement de encoding y una statement meta http-equiv (por ejemplo, ). En agentes de usuario que se ajustan a XHTML, el valor de la statement de encoding de la statement XML tiene prioridad.

Si no hay statement de encoding de caracteres, XML define UTF-8 y UTF-16 como encoding de caracteres predeterminada :

A menos que una encoding esté determinada por un protocolo de nivel superior, también es un error fatal si una entidad XML no contiene ninguna statement de encoding y su contenido no es legal UTF-8 o UTF-16.

Entonces, para resumir, el orden es:

  1. Un parámetro HTTP “charset” en un campo “Content-Type”.
  2. Declaración XML con atributo de encoding .
  3. Una statement META con “http-equiv” establecido en “Content-Type” y un valor establecido para “charset”.

Si no está presente ninguna statement de encoding de caracteres, puede suponer que ISO 8859-1 es la encoding predeterminada para HTML y debe suponer UTF-8 o UTF-16 como encoding predeterminada para XHTML.