如何纠正MySQL utf8_general_ci字段中的双编码UTF-8字符串?

时间:2011-05-10 14:44:53

标签: php mysql utf-8

我必须重新设计一个类,其中(其中包括)UTF-8字符串被错误地双重编码:

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

这些错误的字符串已保存到MySQL数据库的多个表字段中。受影响的所有字段都使用归类utf8_general_ci

通常我会设置一个小PHP补丁脚本,循环通过受影响的表,选择记录,在双编码字段上使用utf8_decode()更正错误记录并更新它们。

由于这次我得到了许多大桌子,这个错误只会影响德国变音符号(äöüßÄÖÜ),我想知道是否有更智能/更快的解决方案。

以下的纯MySQL解决方案是否安全且值得推荐?

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

任何其他解决方案/最佳做法?

6 个答案:

答案 0 :(得分:20)

更改表格以将列字符集更改为Latin-1。您现在将具有单独编码的UTF-8字符串,但是坐在其校对应该是Latin-1的字段中。

您所做的是,通过二进制字符集将列字符集更改回UTF-8 - 这样MySQL就不会在任何时候转换字符。

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

(正确的语法是iirc;将适当的列类型放在...所在的位置)

答案 1 :(得分:13)

我尝试了发布的解决方案,但我的数据库不断出现错误。最终我偶然发现了以下解决方案(在我认为的论坛中,但我不记得在哪里):

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

它起了作用。希望这可以帮助那些在我这里绝望的谷歌搜索过来的人。

注意:这当然是假设您的双重编码字符问题源于从latin1到utf8的过度有用的MySQL转换,但我相信大多数这些“损坏的字符”都会发生。这基本上做了与上面提到的相同的转换回到latin1,然后二进制,然后到utf8(使用二进制步骤作为防止已经编码的latin1实体的重新编码的方式)

答案 2 :(得分:7)

我发现以下方法更简单:

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

然后删除所有表并使用以下命令重新导入:

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

在此网址找到提示: http://blog.hno3.org/2010/04/22/fixing-double-encoded-utf-8-data-in-mysql/

答案 3 :(得分:1)

MySql是charset意识,因此您可以在SQL中进行转换。但对于这种情况,我可能更喜欢用PHP编写脚本,因为它无论如何都是一次性的任务。

请记住,MySql中的列具有charset属性。整理(理论上)与字符集正交。虽然utf8_general_ci排序规则暗示该字符集为utf8,但它不是给定的。理论上你可以将utf8校对与latin1编码混合在一起(并因此得到垃圾)。

如果您决定在SQL中执行此操作,请查看此处:

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

答案 4 :(得分:0)

MySQL提供正则表达式匹配,但没有正则表达式替换,所以通常最好迭代遍历php中的每一行,根据需要进行转换,如果已经更改了行,则更新该行。

答案 5 :(得分:0)

使用mysqldump生成转储,更改编码声明(在第一个命令中),然后在另一个数据库中重新加载。

您还可以使用转储上的iconv对其进行转码。

你可以选择INTO OUTFILE,使用php或iconv按摩文件,然后加载数据INFILE。