我的数据库中的“UTF8”数据是否真的编码正确?

时间:2015-06-12 22:28:55

标签: php mysql utf-8

我有一个带有MYSQL数据库的PHP应用程序,“应该”包含UTF8编码数据。关于unicode字符,我的应用程序似乎从头到尾都能正常工作。如果有人将“Strömgren”提交到我的数据库中(通过HTML表单),我会在收到数据后看到“Strömgren”等。

我的数据库表都是UTF8,我的html页面和表单都是charset = utf-8。

我最近注意到,在我的应用程序的一部分中,我的unicode字符似乎是双重编码的。当我展示应该是Strömgren时,我看到了Strömgren - Str \ xc3 \ xb6mgren vs Str \ xc3 \ x83 \ xc2 \ xb6mgren。如果我utf8_decode坏字符串,它看起来再次正确。

我假设这是“双重编码”。

我发现显示双重编码数据的应用程序部分使用不同的代码来建立数据库连接,并且该代码正在进行此调用:

$ DB-> set_charset( “UTF8”)

我原本打算为我的所有数据库连接执行此操作,但不知何故最终只在一个地方执行此操作。所以,我的几乎所有应用程序都在没有set_charset命令的情况下使用连接,并且Strömgren总是看起来正确,而且只有一段代码确实有set_charset(“utf8”)(并且它只从db读取,从不写入它),显示不正确。

我不确定该怎么做,但我怀疑我的数据库中的数据是不是真的存储在UTF8编码中?也许当我发送它Strömgren(没有set_charset(“utf8”))时,它认为它正在接收latin1(或其他),当我读回来时我得到了latin1,但是因为我的html页面有“charset = utf” -8“当它真的被数据库认为它发送给我Strö¶mren时,它被错误地显示为Strömgren。 (我可能没有正确或清楚地说,但我希望可以理解。)

我有两个问题:

首先,我的任何想法是否有意义,还是我完全脱离基础?

其次,我最好的方法是确定数据库中的数据是否被错误编码(即数据库实际上是否包含Strömgren或Strömgren)?

3 个答案:

答案 0 :(得分:4)

查看实际存储内容的一种方法是使用 HEX 功能。 (这是最接近Oracle风格的DUMP()函数。

这是一个演示,显示使用HEX函数返回存储的内容......

  CREATE TABLE foo 
  ( foo_lat VARCHAR(10) CHARSET latin1
  , foo_utf VARCHAR(10) CHARSET utf8
  );

  INSERT INTO foo (foo_lat, foo_utf) VALUES
  ( UNHEX('6dc3b1c3b6'), UNHEX('6dc3b1c3b6') );

  SELECT foo_lat
       , foo_utf
       , HEX(foo_lat)
       , HEX(foo_utf)
    FROM foo ;

foo_lat    foo_utf  HEX(foo_lat)  HEX(foo_utf)  
---------  -------  ------------  --------------
mñö      mñö      6DC3B1C3B6    6DC3B1C3B6   

你的想法似乎很清楚。

set_charset函数是使用msyqli接口指定客户端字符集的推荐方法。

在你运行之前,我很好奇角色是什么。

  $db->character_set_name();

我也好奇......从同一个连接,以下查询返回的内容。

 SELECT @@session.character_set_client
      , @@session.character_set_connection
      , @@session.character_set_results
      , @@session.character_set_server
      , @@global.character_set_client
      , @@global.character_set_connection
      , @@global.character_set_results
      , @@global.character_set_system

...来自显示“正确”字符的示例代码的副本,以及在执行{之后之前“错误地”显示字符的示例代码的副本{1}}。

如果您在任何地方看到set_charset,那可能会出现问题。

如果latin1列中存储了UTF-8个编码值,则表示存在问题。当您使用latin1个字符集从数据库中提取这些值时,这些值将被“双重编码”。

因此,请验证列上的字符集是utf8

警告:如果utf8列中存储了UTF-8个值,请不要尝试将列转换为latin来修复问题,通过对存储的值进行双重编码,会使问题变得更糟。

如果您想要使用它,请在单独的测试数据库上执行此操作;这可能是测试是否将mysqldump备份还原到另一台 test 计算机上的另一个 test MySQL实例的工作的好时机。如果mysqldump生成的.sql文件已经被删除了,那么当你真正需要进行恢复时,你想要现在而不是之后找到它。)

注意:列定义中的字符集非常重要。表上的设置只是在列上未指定时使用的默认值。数据库级别的设置只是在没有指定字符集的情况下创建表时使用的默认

也就是说,更改数据库的字符集不会影响现有的表和列。它会对未指定字符集的任何utf8产生影响。

CREATE TABLE是查看表格和列的实际字符集的便捷方式。

答案 1 :(得分:0)

每个表都有一个默认字符集和一个用于存储其值的排序规则。 使用以下方法找到它们:

SHOW FULL COLUMNS FROM table_name;

SHOW CREATE TABLE table_name;

然后你可以改变一个表,使UTF-8像这样:

ALTER TABLE tbl_name
CONVERT TO CHARACTER SET 'UTF-8'

修复定义为latin1并使用UTF-8数据填充的表的编码:

ALTER TABLE table_name CHANGE field field blob;
ALTER TABLE table_name CHANGE field field text charset utf8;

答案 2 :(得分:0)

Strömgren代替Strömgren暗示Mojibake。

如果SELECT HEX(...) FROM ...为您提供53 74 72 C3B6 6D 67 72 65 6E(没有空格),则表示您已正确存储了utf8编码。 C3B6ö的utf8十六进制。

“双重编码”会显示53 74 72 C383 C2B6 6D 67 72 65 6E其中C383C2B6Ã的utf8十六进制。

请参阅duplicate以获取讨论和解决方案,包括如何通过一对ALTER TABLEs恢复数据。

也就是说,何塞和斯宾塞都有完整答案的元素。