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

Ric*_*ree 4 php mysql utf-8

我有一个带有 MYSQL 数据库的 PHP 应用程序,该数据库“应该”包含 UTF8 编码的数据。对于 unicode 字符,我的应用程序似乎从头到尾都能正常工作。如果有人将“Str\xc3\xb6mgren”提交到我的数据库(通过 HTML 表单),当我取回数据时,我会看到“Str\xc3\xb6mgren”等。

\n\n

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

\n\n

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

\n\n

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

\n\n

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

\n\n

$db->set_charset("utf8")

\n\n

我本来打算对所有数据库连接执行此操作,但不知何故最终只在一个地方执行此操作。因此,我的几乎所有应用程序都使用不带 set_charset 命令的连接,并且 Str\xc3\xb6mgren 看起来总是正确的,而唯一的一段代码确实具有 set_charset("utf8") (并且仅从数据库读取,从不写入),显示不正确。

\n\n

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

\n\n

我有两个问题:

\n\n

首先,我的想法是否有意义,或者我完全没有根据?

\n\n

其次,确定数据库中的数据是否编码错误(即数据库实际上包含 Str\xc3\xb6mgren 或 Str\xc3\x83\xc2\xb6mgren)的最佳方法是什么?

\n

spe*_*593 5

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

\n\n

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

\n\n
  CREATE TABLE foo \n  ( foo_lat VARCHAR(10) CHARSET latin1\n  , foo_utf VARCHAR(10) CHARSET utf8\n  );\n\n  INSERT INTO foo (foo_lat, foo_utf) VALUES\n  ( UNHEX(\'6dc3b1c3b6\'), UNHEX(\'6dc3b1c3b6\') );\n\n  SELECT foo_lat\n       , foo_utf\n       , HEX(foo_lat)\n       , HEX(foo_utf)\n    FROM foo ;\n\nfoo_lat    foo_utf  HEX(foo_lat)  HEX(foo_utf)  \n---------  -------  ------------  --------------\nm\xc3\x83\xc2\xb1\xc3\x83\xc2\xb6      m\xc3\xb1\xc3\xb6      6DC3B1C3B6    6DC3B1C3B6   \n
Run Code Online (Sandbox Code Playgroud)\n\n
\n\n

你的想法看起来很清楚。

\n\n

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

\n\n

在运行之前我有点好奇字符集是什么。

\n\n
  $db->character_set_name();\n
Run Code Online (Sandbox Code Playgroud)\n\n

我也很好奇......从同一个连接,以下查询返回什么。

\n\n
 SELECT @@session.character_set_client\n      , @@session.character_set_connection\n      , @@session.character_set_results\n      , @@session.character_set_server\n      , @@global.character_set_client\n      , @@global.character_set_connection\n      , @@global.character_set_results\n      , @@global.character_set_system\n
Run Code Online (Sandbox Code Playgroud)\n\n

...执行set_charset.

\n\n

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

\n\n

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

\n\n

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

\n\n

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

\n\n

如果您想尝试一下,请在单独的测试数据库上进行;现在可能是测试是否将 mysqldump 备份恢复到另一个测试中的另一个测试MySQL 实例的好时机是否正常工作的好时机。如果 mysqldump 生成的 .sql 文件被乱码,您希望现在就发现它,而不是稍后,当您真正需要进行恢复时。)

\n\n
\n\n

注意:重要的是列定义上的字符集。表上的设置只是未在列上指定时使用的默认值。并且数据库级别的设置只是默认的在创建表时未指定字符集时使用的

\n\n

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

\n\n

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

\n