2011-05-10 9 views
14

que tienen que rediseñar una clase donde (entre otras cosas) cadenas UTF-8 son de doble codificado erróneamente:¿Cómo corregir cadenas de doble codificación UTF-8 ubicadas en los campos de MySQL utf8_general_ci?

$string = iconv('ISO-8859-1', 'UTF-8', $string); 
: 
$string = utf8_encode($string); 

Estas cadenas defectuosas se han guardado en múltiples campos de la tabla por toda una base de datos MySQL. Todos los campos afectados se usan la intercalación utf8_general_ci.

Normalmente configuraba un pequeño script de parche PHP, recorriendo las tablas afectadas, SELECCIONANDO los registros, corrigiendo los registros defectuosos usando utf8_decode() en los campos de doble codificación y ACTUALIZANDO los mismos.

Como esta vez obtuve muchas y enormes tablas, y el error solo afecta las diéresis alemanas (äöüßÄÖÜ), me pregunto si hay una solución más inteligente/más rápida que eso.

¿Son las soluciones puras de MySQL como las siguientes seguras y recomendables?

UPDATE `table` SET `col` = REPLACE(`col`, 'ä', 'ä'); 

¿Alguna otra solución/mejores prácticas?

Respuesta

20

modificar la tabla para cambiar el carácter conjunto de columnas con Latin-1. Ahora tendrá cadenas UTF-8 codificadas individualmente, pero sentado en un campo cuya intercalación se supone que es Latin-1.

Lo que hace entonces es cambiar el conjunto de caracteres de columna a UTF-8 a través del juego de caracteres binarios - de esa manera MySQL no convierte los caracteres en ningún punto.

ALTER TABLE MyTable MODIFY MyColumn ... CHARACTER SET latin1 
ALTER TABLE MyTable MODIFY MyColumn ... CHARACTER SET binary 
ALTER TABLE MyTable MODIFY MyColumn ... CHARACTER SET utf8 

(es el IIRC sintaxis correcta, poner el tipo de columna apropiado en donde ... es)

+0

D'oh, .. éste literalmente 'me golpeó' fuera de la silla (juego de palabras ^^)! Gracias por sacar esto de tu bolsa mágica. Funcionó como un amuleto y me desactivó de cualquier preocupación sobre problemas de "falta de memoria". Solo tuve que usar 'CHANGE' en lugar de' MODIFY'. Por cierto, noté que al cambiar a charset 'latin1' implícitamente cambió la intercalación a' latin1_swedish_ci'. ¿Sería más seguro agregar 'COLLATE latin1_swedish_ci' para forzar esto? Lo mismo ocurre con 'binary' (intercalación conmutada a' none') y 'utf8' (la colación conmutada vuelve a' utf8_general_ci'). Gracias de nuevo. Me guardaste la noche ^^ –

+0

Creo que cada vez que cambias el conjunto de caracteres, si no especificas una intercalación, la intercalación será la clasificación predeterminada para el nuevo conjunto de caracteres. La intercalación no debería importar para los primeros dos cambios, porque solo está usando temporalmente ese juego de caracteres, y la intercalación no tiene ninguna relación con AFAIK sobre cómo se almacenan los caracteres en la tabla o qué caracteres se almacenan. Solo la codificación importa en esos dos cambios. Por lo tanto, solo deberá especificar una intercalación para el cambio final, en la que la cambie de nuevo a UTF-8. Pero si me equivoco, doy la bienvenida a que me corrijan. – Hammerite

+0

Gracias por seguir explicando sobre esto. Se encontró una página en la documentación de MySQL que confirma sus opiniones sobre una alternativa [intercalación predeterminada] (http://dev.mysql.com/doc/refman/5.6/en/charset-table.html). Forzar una intercalación en el último cambio solo tiene sentido para mí también. Lo hará –

0

MySQL proporciona una coincidencia regexp pero no regexp, por lo que generalmente es mejor iterar a través de cada fila en php, convertir según sea necesario y actualizar la fila si se ha modificado.

1

MySql es consciente de los caracteres, por lo que puede convertir en SQL. Pero para este caso, probablemente preferiría simplemente crear un script en PHP, ya que es una tarea única de todos modos.

Tenga en cuenta que las columnas en MySql tienen una propiedad de juego de caracteres. La intercalación es (en teoría) ortogonal al juego de caracteres. Mientras que una intercalación utf8_general_ci sería implicar que el juego de caracteres es utf8, no es un hecho. En teoría, podría mezclar una intercalación utf8 con una codificación latin1 (y obtener basura como resultado).

Si decide hacer esto en SQL, mira aquí:

http://dev.mysql.com/doc/refman/5.0/en/charset-convert.html

+0

Para ser sincero, hasta que lo mencionó en su segundo párrafo, siempre pensé que la intercalación 'utf8_general_ci' __does__ significa charset' utf8' al mismo tiempo. +1 por hacerme leer la documentación básica de MySQL nuevamente después de décadas (con más cuidado esta vez ^^). Muchas gracias por eso. –

0

generar un volcado usando mysqldump, cambie la declaración de codificación (que se encuentra en los primeros comandos), y vuelve a cargar en otro base de datos.

También puede usar iconv en su volcado para transcodificarlo.

Puede SELECCIONAR EN PERFIL DE SALIDA, masajear el archivo usando php o iconv, luego LOAD DATA INFILE.

+0

Pensé en 'utf8_decode' como un tugurio completo, pero lo descarté. Simplemente porque algunas tablas son demasiado grandes (1G + cada una), lo que me obligaría a leer y convertir todo el volcado línea por línea. Gracias de cualquier manera. –

13

Probé las soluciones publicadas, pero mi DB siguió arrojando errores.Con el tiempo me encontré con la siguiente solución (en un foro creo, pero no puedo recordar dónde):

UPDATE table_name SET col_name = CONVERT(CONVERT(CONVERT(col_name USING latin1) USING binary) USING utf8); 

y funcionó un placer. Espero que esto ayude a cualquiera que haya tropezado aquí con búsquedas desesperadas de google como yo.

NOTA: Esto, por supuesto, suponiendo que sus problemas de doble codificación provienen de una conversión de MySQL demasiado útil de latin1 a utf8, pero creo que es donde ocurre la mayoría de estos "caracteres corruptos". Esto básicamente hace la misma conversión que mencioné anteriormente a latin1, luego binary, luego a utf8 (usando el paso binario como una manera de prevenir la recodificación de las entidades latin1 ya codificadas)

+0

Usted me salvó la vida: D – Foxsk8

7

Encontré el siguiente enfoque más simple :

mysqldump -h DB_HOST -u DB_USER -p --skip-set-charset --default-character-set=latin1 DB_NAME > DB_NAME-dump.sql 

a continuación, colocar todas las tablas y vuelva a importar con el siguiente comando:

mysql -h DB_HOST -u DB_USER -p --default-character-set=utf8 DB_NAME < DB_NAME-dump.sql 

Consejo se encuentra en esta dirección: http://blog.hno3.org/2010/04/22/fixing-double-encoded-utf-8-data-in-mysql/

+0

Esta solución fue más fácil y mucho más rápida que las otras soluciones aquí. –

+0

Esto funcionó muy bien ... en columnas de doble codificación. Echó a perder las columnas correctamente codificadas que tenía (no preguntes), cambiando todo el cirílico a ??????????????????? 's. Fácil de arreglar copiando el texto recién recodificado en la tabla anterior. Gracias. – ow3n

Cuestiones relacionadas