Solución:
Si aplica utf8_encode()
a una cadena ya UTF-8, devolverá una salida UTF-8 distorsionada.
Hice una función que aborda todos estos problemas. Se llama Encoding::toUTF8()
.
No necesita saber cuál es la codificación de sus cadenas. Puede ser Latin1 (ISO 8859-1), Windows-1252 o UTF-8, o la cadena puede tener una combinación de ellos. Encoding::toUTF8()
convertirá todo a UTF-8.
Lo hice porque un servicio me estaba dando una fuente de datos en mal estado, mezclando UTF-8 y Latin1 en la misma cadena.
Uso:
require_once('Encoding.php');
use ForceUTF8Encoding; // It's namespaced now.
$utf8_string = Encoding::toUTF8($utf8_or_latin1_or_mixed_string);
$latin1_string = Encoding::toLatin1($utf8_or_latin1_or_mixed_string);
Descargar:
https://github.com/neitanod/forceutf8
He incluido otra función, Encoding::fixUFT8()
, que solucionará todas las cuerdas UTF-8 que se vean distorsionadas.
Uso:
require_once('Encoding.php');
use ForceUTF8Encoding; // It's namespaced now.
$utf8_string = Encoding::fixUTF8($garbled_utf8_string);
Ejemplos:
echo Encoding::fixUTF8("Fédération Camerounaise de Football");
echo Encoding::fixUTF8("Fédération Camerounaise de Football");
echo Encoding::fixUTF8("FÃÂédÃÂération Camerounaise de Football");
echo Encoding::fixUTF8("Fédération Camerounaise de Football");
dará salida:
Fédération Camerounaise de Football
Fédération Camerounaise de Football
Fédération Camerounaise de Football
Fédération Camerounaise de Football
He transformado la función (forceUTF8
) en una familia de funciones estáticas en una clase llamada Encoding
. La nueva función es Encoding::toUTF8()
.
Primero debe detectar qué codificación se ha utilizado. Mientras analiza los feeds RSS (probablemente a través de HTTP), debería leer la codificación del charset
parámetro de la Content-Type
Campo de encabezado HTTP. Si no está presente, lea la codificación del encoding
atributo de la instrucción de procesamiento XML. Si eso también falta, use UTF-8 como se define en la especificación.
Editar Esto es lo que probablemente haría:
Usaría cURL para enviar y buscar la respuesta. Eso le permite establecer campos de encabezado específicos y obtener el encabezado de respuesta también. Después de obtener la respuesta, debe analizar la respuesta HTTP y dividirla en encabezado y cuerpo. El encabezado debe contener el Content-Type
campo de encabezado que contiene el tipo MIME y (con suerte) el charset
parámetro con la codificación / juego de caracteres también. De lo contrario, analizaremos el PI XML para detectar la presencia del encoding
atributo y obtenga la codificación desde allí. Si eso también falta, las especificaciones XML definen usar UTF-8 como codificación.
$url="http://www.lr-online.de/storage/rss/rss/sport.xml";
$accept = array(
'type' => array('application/rss+xml', 'application/xml', 'application/rdf+xml', 'text/xml'),
'charset' => array_diff(mb_list_encodings(), array('pass', 'auto', 'wchar', 'byte2be', 'byte2le', 'byte4be', 'byte4le', 'BASE64', 'UUENCODE', 'HTML-ENTITIES', 'Quoted-Printable', '7bit', '8bit'))
);
$header = array(
'Accept: '.implode(', ', $accept['type']),
'Accept-Charset: '.implode(', ', $accept['charset']),
);
$encoding = null;
$curl = curl_init($url);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_HEADER, true);
curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
$response = curl_exec($curl);
if (!$response) {
// error fetching the response
} else {
$offset = strpos($response, "rnrn");
$header = substr($response, 0, $offset);
if (!$header || !preg_match('/^Content-Type:s+([^;]+)(?:;s*charset=(.*))?/im', $header, $match)) {
// error parsing the response
} else {
if (!in_array(strtolower($match[1]), array_map('strtolower', $accept['type']))) {
// type not accepted
}
$encoding = trim($match[2], '"'');
}
if (!$encoding) {
$body = substr($response, $offset + 4);
if (preg_match('/^<?xmls+version=(?:"[^"]*"|'[^']*')s+encoding=("[^"]*"|'[^']*')/s', $body, $match)) {
$encoding = trim($match[1], '"'');
}
}
if (!$encoding) {
$encoding = 'utf-8';
} else {
if (!in_array($encoding, array_map('strtolower', $accept['charset']))) {
// encoding not accepted
}
if ($encoding != 'utf-8') {
$body = mb_convert_encoding($body, 'utf-8', $encoding);
}
}
$simpleXML = simplexml_load_string($body, null, LIBXML_NOERROR);
if (!$simpleXML) {
// parse error
} else {
echo $simpleXML->asXML();
}
}
Detectar la codificación es difícil.
mb_detect_encoding
funciona adivinando, basándose en una cantidad de candidatos que lo aprueba. En algunas codificaciones, ciertas secuencias de bytes no son válidas y, por lo tanto, pueden distinguir entre varios candidatos. Desafortunadamente, hay muchas codificaciones, donde los mismos bytes son válidos (pero diferentes). En estos casos, no hay forma de determinar la codificación; Puede implementar su propia lógica para hacer conjeturas en estos casos. Por ejemplo, es más probable que los datos que provienen de un sitio japonés tengan una codificación japonesa.
Siempre que solo se ocupe de los idiomas de Europa occidental, las tres codificaciones principales a considerar son utf-8
, iso-8859-1
y cp-1252
. Dado que estos son valores predeterminados para muchas plataformas, también es más probable que se denuncien incorrectamente. P.ej. si las personas usan diferentes codificaciones, es probable que sean francas al respecto, ya que de lo contrario su software se rompería con mucha frecuencia. Por lo tanto, una buena estrategia es confiar en el proveedor, a menos que la codificación se informe como una de esas tres. Aún debe verificar que sea realmente válido, usando mb_check_encoding
(tenga en cuenta que válido no es lo mismo que ser – la misma entrada puede ser válida para muchas codificaciones). Si es uno de esos, puede usar mb_detect_encoding
para distinguir entre ellos. Afortunadamente, eso es bastante determinista; Solo necesita usar la secuencia de detección adecuada, que es UTF-8,ISO-8859-1,WINDOWS-1252
.
Una vez que haya detectado la codificación, debe convertirla a su representación interna (UTF-8
es la única opción sensata). La función utf8_encode
transforma ISO-8859-1
para UTF-8
, por lo que solo se puede usar para ese tipo de entrada en particular. Para otras codificaciones, utilice mb_convert_encoding
.