【问题标题】:Is the "UTF8" data in my database really encoded correctly?我的数据库中的“UTF8”数据真的编码正确吗?
【发布时间】:2015-08-29 01:16:54
【问题描述】:

我有一个带有“应该”包含 UTF8 编码数据的 MYSQL 数据库的 PHP 应用程序。关于 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") (并且只从数据库中读取,从不写入它),显示不正确。

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

我有两个问题:

首先,我在这里的任何想法是否有道理,或者我完全脱离了基础?

其次,确定数据库中的数据是否编码错误(即数据库实际上包含 Strömgren 还是 Strömgren)的最佳方法是什么?

【问题讨论】:

标签: php mysql utf-8


【解决方案1】:

查看实际存储内容的一种方法是使用 HEX 函数。 (这是 MySQL 最接近 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

... 来自“正确”显示字符的示例代码副本和“错误”显示字符的示例代码副本,在您执行 @ 之前 987654327@.

如果您在任何地方看到latin1,那可能是个问题。

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

因此,请验证您在列中的字符集是否为 utf8

警告:如果您确实在 latin 列中存储了 UTF-8 值,请勿尝试通过将列转换为 utf8 来解决问题,这会使问题变得更糟对存储的值进行双重编码。

如果您想尝试一下,请在 单独的 测试数据库上执行此操作;现在可能是测试将您的 mysqldump 备份恢复到另一台 test 机器上的另一个 test MySQL 实例是否正常工作的好时机。如果 mysqldump 生成的 .sql 文件被搞砸了,你现在就想找出它,而不是等到你真正需要恢复的时候。)


注意:重要的是列定义中的字符集。表上的设置只是列上未指定时使用的 default 值。并且数据库级别的设置只是一个默认,当创建一个没有指定字符集的表时使用它。

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

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

【讨论】:

  • 非常感谢。我确认表是 UTF8 并且我使用了 SELECT HEX(last_name) 并得到了可靠的确认,即表中的数据不是我想象的那样。几个月来,我在写作和阅读中使用不正确编码的连接一直在掩盖这个问题。下一个?找到修复数据的方法(一旦我修复了应用程序)。该页面提供了丰富的信息:artur.ejsmont.org/blog/content/…
【解决方案2】:

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

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;

【讨论】:

  • 我知道我的表是 UTF-8。我首先需要做的是找出如何通过非 utf8 连接来确认我已经用双编码数据填充了我的表。我正在考虑类似的事情,除了通过我的 PHP 脚本之外,有没有一种方法可以连接到数据库,选择一行,看看我得到的是 Strömgren 还是 Strömgren,并确定我的数据是搞砸了,或者我需要寻找其他解释。
  • 好故事,但它没有回答问题。 OP 可能知道如何更改字符集,但是在您将其设置为 UTF-8 之后,您仍然可以对结果进行双重、三重或 gazipple 编码。
  • 我过去曾把编码弄得一团糟,而且它……一团糟。你应该使用原始编码来处理结果,否则你会遇到麻烦。
  • 如果连接字符集和表字符集相等,您可以假设 MYSQL 不进行任何字符集转换。所以字符串的表示完全依赖于 HTML 端。当您开始使用 $db->set_charset("utf8") 时,MYSQL 正在将存储在 latin1 字段中的 utf-8 字符串转换为双倍编码的 utf-8 字符串。
【解决方案3】:

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 恢复数据。

也就是说,Jose 和 Spencer 都有完整答案的要素。

【讨论】:

  • 我认为你读错了。 SELECT HEX 确认数据库包含双重编码的 UTF8。数据库中包含的字符串正是您正确所说的,如果它是双重编码的。没有 mojibake(但感谢你教我一个新词!)。
猜你喜欢
  • 1970-01-01
  • 2016-12-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-12-30
  • 1970-01-01
  • 2019-12-10
  • 1970-01-01
相关资源
最近更新 更多