PHP/MySQL:修复隐式 mysqli::set_charset('latin1') 连接损坏的 utf8 文本
PHP/MySQL: Fixing utf8 text corrupted by implicit mysqli::set_charset('latin1') connection
所以,多年来,我的 PHP 应用程序一直使用默认的 latin1
字符集连接到 MySQL。尽管我将一些字段整理为 utf8_general_ci
,但存储到它们中的实际数据是一些混杂的字符集。例如:
输入:♠ »
存储为♠»
现在,当通过相同的 latin1
连接检索该数据并显示在编码设置为 utf8
的页面上时,它会像输入时一样显示:♠ »
为什么这是,我不是 100% 确定,但我猜这是因为无论什么字符集函数搞砸了它,都会修复它。
我想修复我的数据。如果我使用 mysqli::set_charset('utf8')
切换我的连接字符集,输出将按存储时的形式显示,即 ♠»
所以,显然我需要修复我现有的数据,然后切换我的连接字符集。
如何修复现有的混蛋数据?
编辑:
I've discovered a way to emulate the corruption process that is
happening in a MySQL query: SELECT CAST(BINARY '♠ »' AS CHAR CHARACTER SET latin1)
outputs ♠»
Perhaps if I could figure out how to perform the reverse function I could use that query to fix the existing data.
编辑 2:
I've discovered such a function: SELECT CAST(BINARY CAST('♠»' AS CHAR CHARACTER SET latin1) AS CHAR CHARACTER SET utf8)
outputs ♠ »
My only concern now is what this will do to any data that already
happens to be actual utf8 data, which, for some reason, I do have in
my database. For example, SELECT CAST(BINARY CAST('♠ »' AS CHAR CHARACTER SET latin1) AS CHAR CHARACTER SET utf8)
outputs (nothing)
来自http://jonisalonen.com/2012/fixing-doubly-utf-8-encoded-text-in-mysql/:
将可能损坏的 latin1 文本数据转换为 utf8 的自动检测功能:
DELIMITER $$
CREATE FUNCTION maybe_utf8_decode(str text charset utf8)
RETURNS text CHARSET utf8 DETERMINISTIC
BEGIN
declare str_converted text charset utf8;
declare max_error_count int default @@max_error_count;
set @@max_error_count = 0;
set str_converted = convert(binary convert(str using latin1) using utf8);
set @@max_error_count = max_error_count;
if @@warning_count > 0 then
return str;
else
return str_converted;
end if;
END$$
DELIMITER ;
用法:
update mytable set mycolumn = maybe_utf8_decode(mycolumn);
在尝试 "fix" 数据之前,请确保您拥有的是什么。 SELECT col, HEX(col) ...
-- ♠
可能是 3 个字节:E299A0
,或者可能更多:C3A2 E284A2 C2A0
。前者是Mojibake;后者是"double encoding"。维修方式不同。更多讨论 and here.
所以,多年来,我的 PHP 应用程序一直使用默认的 latin1
字符集连接到 MySQL。尽管我将一些字段整理为 utf8_general_ci
,但存储到它们中的实际数据是一些混杂的字符集。例如:
输入:♠ »
存储为♠»
现在,当通过相同的 latin1
连接检索该数据并显示在编码设置为 utf8
的页面上时,它会像输入时一样显示:♠ »
为什么这是,我不是 100% 确定,但我猜这是因为无论什么字符集函数搞砸了它,都会修复它。
我想修复我的数据。如果我使用 mysqli::set_charset('utf8')
切换我的连接字符集,输出将按存储时的形式显示,即 ♠»
所以,显然我需要修复我现有的数据,然后切换我的连接字符集。
如何修复现有的混蛋数据?
编辑:
I've discovered a way to emulate the corruption process that is happening in a MySQL query:
SELECT CAST(BINARY '♠ »' AS CHAR CHARACTER SET latin1)
outputs♠»
Perhaps if I could figure out how to perform the reverse function I could use that query to fix the existing data.
编辑 2:
I've discovered such a function:
SELECT CAST(BINARY CAST('♠»' AS CHAR CHARACTER SET latin1) AS CHAR CHARACTER SET utf8)
outputs♠ »
My only concern now is what this will do to any data that already happens to be actual utf8 data, which, for some reason, I do have in my database. For example,
SELECT CAST(BINARY CAST('♠ »' AS CHAR CHARACTER SET latin1) AS CHAR CHARACTER SET utf8)
outputs (nothing)
来自http://jonisalonen.com/2012/fixing-doubly-utf-8-encoded-text-in-mysql/:
将可能损坏的 latin1 文本数据转换为 utf8 的自动检测功能:
DELIMITER $$
CREATE FUNCTION maybe_utf8_decode(str text charset utf8)
RETURNS text CHARSET utf8 DETERMINISTIC
BEGIN
declare str_converted text charset utf8;
declare max_error_count int default @@max_error_count;
set @@max_error_count = 0;
set str_converted = convert(binary convert(str using latin1) using utf8);
set @@max_error_count = max_error_count;
if @@warning_count > 0 then
return str;
else
return str_converted;
end if;
END$$
DELIMITER ;
用法:
update mytable set mycolumn = maybe_utf8_decode(mycolumn);
在尝试 "fix" 数据之前,请确保您拥有的是什么。 SELECT col, HEX(col) ...
-- ♠
可能是 3 个字节:E299A0
,或者可能更多:C3A2 E284A2 C2A0
。前者是Mojibake;后者是"double encoding"。维修方式不同。更多讨论