Detecta la codificación y crea todo UTF-8


304

Estoy leyendo muchos textos de varios canales RSS y los inserto en mi base de datos.

Por supuesto, hay varias codificaciones de caracteres diferentes utilizadas en las fuentes, por ejemplo, UTF-8 e ISO 8859-1.

Desafortunadamente, a veces hay problemas con las codificaciones de los textos. Ejemplo:

  1. El "ß" en "Fußball" debería verse así en mi base de datos: "Ÿ". Si es un "Ÿ", se muestra correctamente.

  2. A veces, el "ß" en "Fußball" se ve así en mi base de datos: "ß". Entonces se muestra incorrectamente, por supuesto.

  3. En otros casos, el "ß" se guarda como un "ß", es decir, sin ningún cambio. Entonces también se muestra incorrectamente.

¿Qué puedo hacer para evitar los casos 2 y 3?

¿Cómo puedo hacer que todo tenga la misma codificación, preferiblemente UTF-8? ¿Cuándo debo usar utf8_encode(), cuándo debo usar utf8_decode()(está claro cuál es el efecto pero cuándo debo usar las funciones?) Y cuándo no debo hacer nada con la entrada?

¿Cómo hago que todo tenga la misma codificación? Quizás con la función mb_detect_encoding()? ¿Puedo escribir una función para esto? Entonces mis problemas son:

  1. ¿Cómo puedo saber qué codificación utiliza el texto?
  2. ¿Cómo lo convierto a UTF-8, sea cual sea la codificación anterior?

¿Funcionaría una función como esta?

function correct_encoding($text) {
    $current_encoding = mb_detect_encoding($text, 'auto');
    $text = iconv($current_encoding, 'UTF-8', $text);
    return $text;
}

Lo he probado, pero no funciona. ¿Qué tiene de malo?


36
"El" ß "en" Fußball "debería verse así en mi base de datos:" Ÿ ".". No, debería verse como ß. Asegúrese de que la intercalación y la conexión estén configuradas correctamente. De lo contrario, la clasificación y la búsqueda se romperán para usted.
Rich Bradshaw

55
Su base de datos está mal configurada. Si desea almacenar contenido Unicode, simplemente configúrelo para eso. Entonces, en lugar de intentar solucionar el problema en su código PHP, primero debe arreglar la base de datos.
dolmen

2
USO: $ from = mb_detect_encoding ($ texto); $ text = mb_convert_encoding ($ text, 'UTF-8', $ desde);
Informate.it

Respuestas:


363

Si aplica utf8_encode()a una cadena ya UTF-8, devolverá una salida UTF-8 ilegible.

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 desordenados, mezclando UTF-8 y Latin1 en la misma cadena.

Uso:

require_once('Encoding.php');
use \ForceUTF8\Encoding;  // 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 arreglará cada cadena UTF-8 que se vea confusa.

Uso:

require_once('Encoding.php');
use \ForceUTF8\Encoding;  // 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().


1
Bueno, si observa el código, fixUTF8 simplemente llama a forceUTF8 una y otra vez hasta que la cadena se devuelve sin cambios. Una llamada a fixUTF8 () toma al menos el doble de tiempo que una llamada a forceUTF8 (), por lo que es mucho menos eficaz. Hice fixUTF8 () solo para crear un programa de línea de comandos que arreglara archivos "codificados corruptos", pero en un entorno en vivo rara vez se necesita.
Sebastián Grignoli

3
¿Cómo convierte esto los caracteres que no son UTF8 a UTF8, sin saber en qué codificación están los caracteres no válidos?
philfreo

44
Asume ISO-8859-1, la respuesta ya lo dice. La única diferencia entre forceUTF8 () y utf8_encode () es que forceUTF8 () reconoce los caracteres UTF8 y los mantiene sin cambios.
Sebastián Grignoli

28
"No necesita saber cuál es la codificación de sus cadenas". - Estoy muy en desacuerdo. Adivinar y probar puede funcionar, pero tarde o temprano siempre encontrarás casos extremos donde no es así.
deceze

44
Estoy totalmente de acuerdo. De hecho, no quise decir eso como una regla general, solo explicar que esta clase podría ayudarte si esa es la situación en la que te encuentras.
Sebastián Grignoli

74

Primero debe detectar qué codificación se ha utilizado. Mientras analiza las fuentes RSS (probablemente a través de HTTP), debe leer la codificación del charsetparámetro del Content-Typecampo de encabezado HTTP . Si no está presente, lea la codificación del encodingatributo de la instrucción de procesamiento XML . Si eso también falta, use UTF-8 como se define en la especificación .


Editar    Aquí es lo que probablemente haría:

Me gustaría usar cURL para enviar a buscar la respuesta. Eso le permite establecer campos de encabezado específicos y buscar el encabezado de respuesta también. Después de buscar la respuesta, debe analizar la respuesta HTTP y dividirla en encabezado y cuerpo. El encabezado debe contener el Content-Typecampo de encabezado que contiene el tipo MIME y (con suerte) el charsetparámetro con la codificación / juego de caracteres también. De lo contrario, analizaremos la PI de XML para determinar la presencia del encodingatributo y obtendremos la codificación a partir de ahí. Si eso también falta, las especificaciones XML definen el uso de 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, "\r\n\r\n");
    $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('/^<\?xml\s+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();
    }
}

Gracias. Esto seria facil. ¿Pero realmente funcionaría? A menudo hay codificaciones incorrectas en los encabezados HTTP o en los atributos de XML.
caw

25
De nuevo: ese no es tu problema. Se establecieron estándares para evitar tales problemas. Si otros no los siguen, es su problema, no el tuyo.
Gumbo

Ok, creo que finalmente me has convencido ahora. :)
caw

Gracias por el codigo. Pero, ¿por qué no simplemente usar esto? paste.bradleygill.com/index.php?paste_id=9651 Su código es mucho más complejo, ¿qué es mejor con él?
caw

Bueno, primero estás haciendo dos solicitudes, una para el encabezado HTTP y otra para los datos. En segundo lugar, usted está en busca de cualquier aspecto de charset=y encoding=y no sólo en las posiciones adecuadas. Y en tercer lugar, no está comprobando si se acepta la codificación declarada.
Gumbo

39

Detectar la codificación es difícil.

mb_detect_encodingfunciona adivinando, en función de una serie de candidatos que lo aprueba. En algunas codificaciones, ciertas secuencias de bytes no son válidas, por lo que puede 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, los datos que provienen de un sitio japonés podrían tener más probabilidades de tener una codificación japonesa.

Mientras solo trate con idiomas de Europa occidental, las tres codificaciones principales a considerar son utf-8, iso-8859-1y cp-1252. Dado que estos son valores predeterminados para muchas plataformas, también es más probable que se denuncien erróneamente. P.ej. Si las personas usan diferentes codificaciones, es probable que sean francos al respecto, ya que de lo contrario su software se rompería muy a menudo. Por lo tanto, una buena estrategia es confiar en el proveedor, a menos que la codificación se informe como uno de esos tres. Aún debe verificar que sea 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 usarmb_detect_encodingpara 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-8es la única opción sensata). La función se utf8_encodetransforma ISO-8859-1en UTF-8, por lo que solo se puede usar para ese tipo de entrada en particular. Para otras codificaciones, use mb_convert_encoding.


¡Muchas gracias! ¿Qué es mejor: mb-convert-encoding () o iconv ()? No sé cuáles son las diferencias. Sí, solo tendré que analizar los idiomas de Europa occidental, especialmente inglés, alemán y francés.
caw

77
Acabo de ver: mb-detect-encoding () no es inútil. Solo es compatible con UTF-8, UTF-7, ASCII, EUC-JP, SJIS, eucJP-win, SJIS-win, JIS e ISO-2022-JP. Los más importantes para mí, ISO-8859-1 y WINDOWS-1252, no son compatibles. Entonces no puedo usar mb-detect-encoding ().
caw

1
Dios, tienes razón. Ha pasado un tiempo desde que lo he usado. Tendrás que escribir tu propio código de detección entonces, o usar una utilidad externa. UTF-8 puede determinarse de manera bastante confiable, porque sus secuencias de escape son bastante características. wp-1252 e iso-8859-1 se pueden distinguir porque wp-1252 puede contener bytes que son ilegales en iso-8859-1. Use Wikipedia para obtener los detalles, o busque en la sección de comentarios de php.net, bajo varias funciones relacionadas con el juego de caracteres.
troelskn

Creo que puedes distinguir las diferentes codificaciones cuando miras las formas en las que surgen los cantos especiales: el "ß" alemán emerge en diferentes formas: a veces "Ÿ", a veces "ß" y a veces "ß". ¿Por qué?
caw

Sí, pero luego debes conocer el contenido de la cadena antes de compararla, y ese tipo de cosas frustra el propósito en primer lugar. El ß alemán aparece de manera diferente porque tiene valores diferentes en diferentes codificaciones. Algunos caracteres están representados de la misma manera en diferentes codificaciones (por ejemplo, todos los caracteres en el conjunto de caracteres ascii están codificados de la misma manera en utf-8, iso-8859- * y wp-1252), siempre y cuando utilice solo esos personajes, todos se ven iguales. Es por eso que a veces se les llama compatibles con ascii.
troelskn

14

Una forma realmente agradable de implementar una isUTF8función se puede encontrar en php.net :

function isUTF8($string) {
    return (utf8_encode(utf8_decode($string)) == $string);
}

16
Desafortunadamente, esto solo funciona cuando la cadena solo consta de caracteres que están incluidos en ISO-8859-1. Pero esto podría funcionar: @iconv ('utf-8', 'utf-8 // IGNORE', $ str) == $ str
Christian Davén

@Christian: De hecho, eso es lo que también recomiendan los autores de High Performance MySQL.
Alix Axel

1
No funciona correctamente: echo (int) isUTF8 ('z'); # 1 echo (int) isUTF8 (NULL); # 1
Yousha Aleayoub

1
Aunque no es perfecto, creo que esta es una buena manera de implementar una comprobación incompleta de UTF-8.
Mateng

1
mb_check_encoding($string, 'UTF-8')
deceze

13

Esta hoja de referencia enumera algunas advertencias comunes relacionadas con el manejo de UTF-8 en PHP: http://developer.loftdigital.com/blog/php-utf-8-cheatsheet

Esta función de detección de caracteres multibyte en una cadena también podría ser útil ( fuente ):


function detectUTF8($string)
{
    return preg_match('%(?:
        [\xC2-\xDF][\x80-\xBF]             # non-overlong 2-byte
        |\xE0[\xA0-\xBF][\x80-\xBF]        # excluding overlongs
        |[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte
        |\xED[\x80-\x9F][\x80-\xBF]        # excluding surrogates
        |\xF0[\x90-\xBF][\x80-\xBF]{2}     # planes 1-3
        |[\xF1-\xF3][\x80-\xBF]{3}         # planes 4-15
        |\xF4[\x80-\x8F][\x80-\xBF]{2}     # plane 16
        )+%xs', 
    $string);
}


2
Creo que eso no funciona correctamente: echo detectUTF8 ('3٣3'); # 1
Yousha Aleayoub

10

Un poco de cabeza. Usted dijo que el "ß" debería mostrarse como "Ÿ" en su base de datos.

Esto probablemente se deba a que está utilizando una base de datos con codificación de caracteres Latin-1 o posiblemente su conexión PHP-MySQL está configurada incorrectamente, esto es, P cree que su MySQL está configurado para usar UTF-8, por lo que envía datos como UTF-8 , pero su MySQL cree que PHP está enviando datos codificados como ISO 8859-1, por lo que una vez más puede intentar codificar sus datos enviados como UTF-8, causando este tipo de problemas.

Echa un vistazo a mysql_set_charset . Te puede ayudar.


4

Su codificación parece codificada en UTF-8 dos veces ; es decir, desde alguna otra codificación, a UTF-8 y nuevamente a UTF-8. Como si tuviera ISO 8859-1, convertido de ISO 8859-1 a UTF-8, y tratado la nueva cadena como ISO 8859-1 para otra conversión a UTF-8.

Aquí hay un pseudocódigo de lo que hiciste:

$inputstring = getFromUser();
$utf8string = iconv($current_encoding, 'utf-8', $inputstring);
$flawedstring = iconv($current_encoding, 'utf-8', $utf8string);

Deberías intentarlo:

  1. detecta la codificación usando mb_detect_encoding()o lo que quieras usar
  2. si es UTF-8, conviértalo a ISO 8859-1 y repita el paso 1
  3. finalmente, vuelva a convertirlo en UTF-8

Eso supone que en la conversión "intermedia" usó ISO 8859-1. Si usó Windows-1252, conviértalo a Windows-1252 (latin1). La codificación fuente original no es importante; la que usaste en la segunda conversión defectuosa es.

Esta es mi suposición sobre lo que sucedió; Hay muy poco más que podría haber hecho para obtener cuatro bytes en lugar de un byte ASCII extendido.

El idioma alemán también utiliza ISO 8859-2 y Windows-1250 (Latin-2).


3

Lo interesante de mb_detect_encodingy mb_convert_encodinges que el orden de las codificaciones que sugieres sí importa:

// $input is actually UTF-8

mb_detect_encoding($input, "UTF-8", "ISO-8859-9, UTF-8");
// ISO-8859-9 (WRONG!)

mb_detect_encoding($input, "UTF-8", "UTF-8, ISO-8859-9");
// UTF-8 (OK)

Por lo tanto, es posible que desee utilizar un orden específico al especificar las codificaciones esperadas. Aún así, tenga en cuenta que esto no es infalible.


2
Esto sucede porque ISO-8859-9 en la práctica aceptará cualquier entrada binaria. Lo mismo ocurre con Windows-1252 y sus amigos. Primero debe probar las codificaciones que pueden no aceptar la entrada.
Mikko Rantalainen

@MikkoRantalainen, sí, supongo que esta parte de los documentos dice algo similar: php.net/manual/en/function.mb-detect-order.php#example-2985
Halil Özgür

Teniendo en cuenta que la especificación HTML WHATWG define Windows 1252 como la codificación predeterminada, debería ser bastante seguro asumirlo if ($input_is_not_UTF8) $input_is_windows1252 = true;. Ver también: html.spec.whatwg.org/multipage/…
Mikko Rantalainen

3

Debe probar el conjunto de caracteres en la entrada ya que las respuestas pueden codificarse con diferentes codificaciones.

Obligo a que todo el contenido se envíe a UTF-8 mediante detección y traducción mediante la siguiente función:

function fixRequestCharset()
{
  $ref = array(&$_GET, &$_POST, &$_REQUEST);
  foreach ($ref as &$var)
  {
    foreach ($var as $key => $val)
    {
      $encoding = mb_detect_encoding($var[$key], mb_detect_order(), true);
      if (!$encoding)
        continue;
      if (strcasecmp($encoding, 'UTF-8') != 0)
      {
        $encoding = iconv($encoding, 'UTF-8', $var[$key]);
        if ($encoding === false)
          continue;
        $var[$key] = $encoding;
      }
    }
  }
}

Esa rutina convertirá todas las variables PHP que provienen del host remoto en UTF-8.

O ignore el valor si la codificación no se pudo detectar o convertir.

Puede personalizarlo según sus necesidades.

Solo invocalo antes de usar las variables.


¿Cuál es el propósito de usar mb_detect_order () sin una lista de codificación aprobada?
giorgio79

El propósito es devolver la matriz ordenada configurada del sistema de codificaciones definidas en php.ini utilizado. Esto es requerido por mb_detect_encoding para completar el tercer parámetro.
cavila

2

Resolver la codificación de caracteres de los canales RSS parece ser complicado . Incluso las páginas web normales a menudo omiten o mienten acerca de su codificación.

Por lo tanto, podría intentar usar la forma correcta de detectar la codificación y luego recurrir a alguna forma de autodetección (adivinar).


No quiero leer la codificación de la información del feed. Por lo tanto, es igual si la información del feed es incorrecta. Me gustaría detectar la codificación del texto.
caw

@ marco92w: No es su problema si la codificación declarada es incorrecta. Las normas no se han establecido por diversión.
Gumbo

1
@Gumbo: pero si estás trabajando en el mundo real tienes que ser capaz de lidiar con cosas como codificaciones declaradas incorrectas. El problema es que es muy difícil adivinar (correctamente) la codificación solo de algún texto. Los estándares son maravillosos, pero muchas (¿la mayoría?) De las páginas / feeds no cumplen con ellos.
Kevin ORourke

@ Kevin ORourke: Exactamente, correcto. Ese es mi problema. @Gumbo: Sí, es mi problema. Quiero leer los feeds y agregarlos. Entonces debo corregir las codificaciones incorrectas.
caw

@ marco92w: Pero no puede corregir la codificación si no conoce la codificación correcta y la codificación actual. Y para eso es la declaración charset/ encoding: describa la codificación en la que están codificados los datos.
Gumbo

2

Sé que esta es una pregunta anterior, pero creo que una respuesta útil nunca está de más. Estaba teniendo problemas con mi codificación entre una aplicación de escritorio, SQLite y las variables GET / POST. Algunos estarían en UTF-8, otros estarían en ASCII, y básicamente todo se arruinaría cuando se involucraran caracteres extranjeros.

Aquí está mi solución. Elimina su OBTENER / POSTAR / SOLICITAR (omití las cookies, pero podría agregarlas si lo desea) en cada carga de la página antes del procesamiento. Funciona bien en un encabezado. PHP lanzará advertencias si no puede detectar la codificación de origen automáticamente, por lo que estas advertencias se suprimen con @ 's.

//Convert everything in our vars to UTF-8 for playing nice with the database...
//Use some auto detection here to help us not double-encode...
//Suppress possible warnings with @'s for when encoding cannot be detected
try
{
    $process = array(&$_GET, &$_POST, &$_REQUEST);
    while (list($key, $val) = each($process)) {
        foreach ($val as $k => $v) {
            unset($process[$key][$k]);
            if (is_array($v)) {
                $process[$key][@mb_convert_encoding($k,'UTF-8','auto')] = $v;
                $process[] = &$process[$key][@mb_convert_encoding($k,'UTF-8','auto')];
            } else {
                $process[$key][@mb_convert_encoding($k,'UTF-8','auto')] = @mb_convert_encoding($v,'UTF-8','auto');
            }
        }
    }
    unset($process);
}
catch(Exception $ex){}

Gracias por la respuesta, jocull. La función mb_convert_encoding () es lo que ya hemos tenido aquí, ¿verdad? ;) Entonces, lo único nuevo en su respuesta son los bucles para cambiar la codificación en todas las variables.
graznar

2

Estuve buscando soluciones para la codificación desde hace siglos , ¡y esta página es probablemente la conclusión de años de búsqueda! Probé algunas de las sugerencias que mencionaste y aquí están mis notas:

Esta es mi cadena de prueba:

¡Esta es una cadena de "escritura escrita", pero necesité más "especiales" para verlos, convertidos por conexión! & ¡Eso es!

Hago un INSERT para guardar esta cadena en una base de datos en un campo que está configurado como utf8_general_ci

El conjunto de caracteres de mi página es UTF-8.

Si hago un INSERTAR así, en mi base de datos, probablemente tenga algunos personajes procedentes de Marte ...

Entonces necesito convertirlos en algún UTF-8 "sano". Lo intenté utf8_encode(), pero aún los personajes extraterrestres estaban invadiendo mi base de datos ...

Así que intenté usar la función forceUTF8publicada en el número 8, pero en la base de datos la cadena guardada se ve así:

esta es una cadena de "escritura escrita", pero no tuve que llamar a alguien especial para verlos, ¡convertidos por fécton! & ¡Eso es!

Entonces, al recopilar más información en esta página y fusionarla con otra información en otras páginas, resolví mi problema con esta solución:

$finallyIDidIt = mb_convert_encoding(
  $string,
  mysql_client_encoding($resourceID),
  mb_detect_encoding($string)
);

Ahora en mi base de datos tengo mi cadena con la codificación correcta.

NOTA: ¡ Solo la nota para cuidar está en funcionamiento mysql_client_encoding! Debe estar conectado a la base de datos, porque esta función quiere un ID de recurso como parámetro.

Pero bueno, acabo de volver a codificar antes de mi INSERT, así que para mí no es un problema.


1
¿Por qué no solo usas UTF-8codificación de cliente para mysql en primer lugar? No necesitaría la conversión manual de esta manera
Esailija

2

Es muy sencillo: cuando se consigue algo que no es UTF-8, debe codificar que en UTF-8.

Por lo tanto, cuando va a buscar una determinada fuente que es ISO 8859-1, analícela utf8_encode.

Sin embargo, si va a buscar una fuente UTF-8, no necesita hacer nada.


¡Gracias! OK, puedo averiguar cómo se codifica el feed usando mb-detect-encoding (), ¿verdad? Pero, ¿qué puedo hacer si el feed es ASCII? utf8-encode () no es solo para ISO-8859-1 a UTF-8, ¿no?
caw

ASCII es un subconjunto de ISO-8859-1 y UTF-8, por lo que usar utf8-encode () no debería hacer un cambio - SI en realidad es solo ASCII
Michael Borgwardt

Entonces, ¿siempre puedo usar utf8_encode si no es UTF-8? Esto sería realmente fácil. El texto que era ASCII según mb-detect-encoding () contenía "& # 228;". ¿Es este un personaje ASCII? ¿O es HTML?
caw

Eso es HTML. En realidad, eso está codificado, por lo que cuando lo imprime en una página determinada, muestra que está bien. Si lo desea, primero puede ut8_encode () y luego html_entity_decode ().
Seb

1
El carácter ß está codificado en UTF-8 con la secuencia de bytes 0xC39F. Interpretada con Windows-1252, esa secuencia representa los dos caracteres  (0xC3) y Ÿ (0x9F). Y si codifica esta secuencia de bytes nuevamente con UTF-8, obtendrá 0xC383 0xC29F lo que representa ß en Windows-1252. Entonces, su error es manejar estos datos codificados en UTF-8 como algo con una codificación distinta a UTF-8. Que esta secuencia de bytes se presente como el personaje que estás viendo es solo una cuestión de interpretación. Si usa otra codificación / juego de caracteres, probablemente verá otros caracteres.
Gumbo

1

php.net/mb_detect_encoding

echo mb_detect_encoding($str, "auto");

o

echo mb_detect_encoding($str, "UTF-8, ASCII, ISO-8859-1");

Realmente no sé cuáles son los resultados, pero te sugiero que solo tomes algunos de tus feeds con diferentes codificaciones y pruebes si mb_detect_encodingfunciona o no.

la actualización
automática es la abreviatura de "ASCII, JIS, UTF-8, EUC-JP, SJIS". devuelve el juego de caracteres detectado, que puede usar para convertir la cadena a utf-8 con iconv .

<?php
function convertToUTF8($str) {
    $enc = mb_detect_encoding($str);

    if ($enc && $enc != 'UTF-8') {
        return iconv($enc, 'UTF-8', $str);
    } else {
        return $str;
    }
}
?>

No lo he probado, así que no hay garantía. y tal vez hay una manera más simple.


Gracias. ¿Cuál es la diferencia entre 'auto' y 'UTF-8, ASCII, ISO-8859-1' como segundo argumento? ¿'Auto' presenta más codificaciones? Entonces sería mejor usar 'auto', ¿no? Si realmente funciona sin errores, solo debo cambiar "ASCII" o "ISO-8859-1" a "UTF-8". ¿Cómo?
caw

2
Su función no funciona bien en todos los casos. A veces recibo un error: Aviso: iconv (): Se detectó un carácter ilegal en la cadena de entrada en ...
caw

1

@harpax que funcionó para mí. En mi caso, esto es lo suficientemente bueno:

if (isUTF8($str)) { 
    echo $str; 
}
else
{
    echo iconv("ISO-8859-1", "UTF-8//TRANSLIT", $str);
}

0

Después de ordenar sus scripts php, no olvide decirle a mysql qué conjunto de caracteres está pasando y le gustaría recibir.

Ejemplo: establecer el conjunto de caracteres utf8

Pasar datos utf8 a una tabla latin1 en una sesión de E / S latin1 da esos desagradables birdfeets. Lo veo cada dos días en las tiendas de comercio electrónico. Atrás y cuarto puede parecer correcto. Pero phpmyadmin mostrará la verdad. Al decirle a mysql qué conjunto de caracteres está pasando, manejará la conversión de datos de mysql por usted.

Cómo recuperar los datos mysql revueltos existentes es otro hilo a tratar. :)


0

Esta versión es para el idioma alemán, pero puede modificar los $ CHARSETS y los $ TESTCHARS

class CharsetDetector
{
private static $CHARSETS = array(
"ISO_8859-1",
"ISO_8859-15",
"CP850"
);
private static $TESTCHARS = array(
"€",
"ä",
"Ä",
"ö",
"Ö",
"ü",
"Ü",
"ß"
);
public static function convert($string)
{
    return self::__iconv($string, self::getCharset($string));
}
public static function getCharset($string)
{
    $normalized = self::__normalize($string);
    if(!strlen($normalized))return "UTF-8";
    $best = "UTF-8";
    $charcountbest = 0;
    foreach (self::$CHARSETS as $charset) {
        $str = self::__iconv($normalized, $charset);
        $charcount = 0;
        $stop   = mb_strlen( $str, "UTF-8");

        for( $idx = 0; $idx < $stop; $idx++)
        {
            $char = mb_substr( $str, $idx, 1, "UTF-8");
            foreach (self::$TESTCHARS as $testchar) {

                if($char == $testchar)
                {

                    $charcount++;
                    break;
                }
            }
        }
        if($charcount>$charcountbest)
        {
            $charcountbest=$charcount;
            $best=$charset;
        }
        //echo $text."<br />";
    }
    return $best;
}
private static function __normalize($str)
{

$len = strlen($str);
$ret = "";
for($i = 0; $i < $len; $i++){
    $c = ord($str[$i]);
    if ($c > 128) {
        if (($c > 247)) $ret .=$str[$i];
        elseif ($c > 239) $bytes = 4;
        elseif ($c > 223) $bytes = 3;
        elseif ($c > 191) $bytes = 2;
        else $ret .=$str[$i];
        if (($i + $bytes) > $len) $ret .=$str[$i];
        $ret2=$str[$i];
        while ($bytes > 1) {
            $i++;
            $b = ord($str[$i]);
            if ($b < 128 || $b > 191) {$ret .=$ret2; $ret2=""; $i+=$bytes-1;$bytes=1; break;}
            else $ret2.=$str[$i];
            $bytes--;
        }
    }
}
return $ret; 
}
private static function __iconv($string, $charset)
{
    return iconv ( $charset, "UTF-8" , $string );
}
}


0

Obtenga codificación de encabezados y conviértalo a utf-8.

$post_url='http://website.domain';

/// Get headers ////////////////////////////////////////////////////////////
function get_headers_curl($url) 
{ 
    $ch = curl_init(); 

    curl_setopt($ch, CURLOPT_URL,            $url); 
    curl_setopt($ch, CURLOPT_HEADER,         true); 
    curl_setopt($ch, CURLOPT_NOBODY,         true); 
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 
    curl_setopt($ch, CURLOPT_TIMEOUT,        15); 

    $r = curl_exec($ch); 
    return $r; 
}
$the_header = get_headers_curl($post_url);
/// check for redirect /////////////////////////////////////////////////
if (preg_match("/Location:/i", $the_header)) {
    $arr = explode('Location:', $the_header);
    $location = $arr[1];

    $location=explode(chr(10), $location);
    $location = $location[0];

$the_header = get_headers_curl(trim($location));
}
/// Get charset /////////////////////////////////////////////////////////////////////
if (preg_match("/charset=/i", $the_header)) {
    $arr = explode('charset=', $the_header);
    $charset = $arr[1];

    $charset=explode(chr(10), $charset);
    $charset = $charset[0];
    }
///////////////////////////////////////////////////////////////////////////////
// echo $charset;

if($charset && $charset!='UTF-8') { $html = iconv($charset, "UTF-8", $html); }

0

Ÿes Mojibake para ß. En su base de datos, puede tener hexadecimal

DF if the column is "latin1",
C39F if the column is utf8 -- OR -- it is latin1, but "double-encoded"
C383C5B8 if double-encoded into a utf8 column

Usted debe no utilizar cualquier codificación / decodificación de funciones en PHP; en su lugar, debe configurar la base de datos y la conexión a ella correctamente.

Si MySQL está involucrado, vea: Problemas con los caracteres utf8; lo que veo no es lo que almacené


0

Encuentro la solución aquí http://deer.org.ua/2009/10/06/1/

class Encoding
{
    /**
     * http://deer.org.ua/2009/10/06/1/
     * @param $string
     * @return null
     */
    public static function detect_encoding($string)
    {
        static $list = ['utf-8', 'windows-1251'];

        foreach ($list as $item) {
            try {
                $sample = iconv($item, $item, $string);
            } catch (\Exception $e) {
                continue;
            }
            if (md5($sample) == md5($string)) {
                return $item;
            }
        }
        return null;
    }
}

$content = file_get_contents($file['tmp_name']);
$encoding = Encoding::detect_encoding($content);
if ($encoding != 'utf-8') {
    $result = iconv($encoding, 'utf-8', $content);
} else {
    $result = $content;
}

Creo que @ es una mala decisión y hago algunos cambios a la solución desde deer.org.ua;


0

La respuesta más votada no funciona. Aquí está el mío y espero que ayude.

function toUTF8($raw) {
    try{
        return mb_convert_encoding($raw, "UTF-8", "auto"); 
    }catch(\Exception $e){
        return mb_convert_encoding($raw, "UTF-8", "GBK"); 
    }
}

1
¿Tiene alguna idea de por qué o cómo sus archivos eran diferentes? ¿Qué partes no te funcionaron? Por ejemplo: los caracteres alemanes en mayúscula no se convirtieron correctamente. Curioso, ¿qué es "GBK"?
SherylHohman

-1

Cuando intentas manejar varios idiomas como el japonés y el coreano, puedes meterte en problemas. mb_convert_encoding con el parámetro 'auto' no funciona bien. Establecer mb_detect_order ('ASCII, UTF-8, JIS, EUC-JP, SJIS, EUC-KR, UHC') no ayuda, ya que detectará EUC- * incorrectamente.

Llegué a la conclusión de que siempre que las cadenas de entrada provengan de HTML, debe usar 'charset' en un meta elemento. Uso Simple HTML DOM Parser porque admite HTML no válido.

El fragmento a continuación extrae el elemento del título de una página web. Si desea convertir toda la página, puede que desee eliminar algunas líneas.

<?php
require_once 'simple_html_dom.php';

echo convert_title_to_utf8(file_get_contents($argv[1])), PHP_EOL;

function convert_title_to_utf8($contents)
{
    $dom = str_get_html($contents);
    $title = $dom->find('title', 0);
    if (empty($title)) {
        return null;
    }
    $title = $title->plaintext;
    $metas = $dom->find('meta');
    $charset = 'auto';
    foreach ($metas as $meta) {
        if (!empty($meta->charset)) { // html5
            $charset = $meta->charset;
        } else if (preg_match('@charset=(.+)@', $meta->content, $match)) {
            $charset = $match[1];
        }
    }
    if (!in_array(strtolower($charset), array_map('strtolower', mb_list_encodings()))) {
        $charset = 'auto';
    }
    return mb_convert_encoding($title, 'UTF-8', $charset);
}

-1

Tuve el mismo problema con phpQuery ( ISO-8859-1 en lugar de UTF-8 ) y este truco me ayudó:

$html = '<?xml version="1.0" encoding="UTF-8" ?>' . $html;

mb_internal_encoding('UTF-8'), phpQuery::newDocumentHTML($html, 'utf-8'), mbstring.internal_encodingY otras manipulaciones no tuvieron ningún efecto.


-1

Probar sin 'auto'

Es decir:

mb_detect_encoding($text)

en vez de:

mb_detect_encoding($text, 'auto')

Puede encontrar más información aquí: mb_detect_encoding

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.