将 UTF-8 编码的字符串插入 UTF-8 编码的 mysql 表失败​​,并显示“字符串值不正确”

Posted

技术标签:

【中文标题】将 UTF-8 编码的字符串插入 UTF-8 编码的 mysql 表失败​​,并显示“字符串值不正确”【英文标题】:Inserting UTF-8 encoded string into UTF-8 encoded mysql table fails with "Incorrect string value" 【发布时间】:2012-08-09 20:07:33 【问题描述】:

将 UTF-8 编码的字符串插入 UTF-8 编码的表会给出不正确的字符串值。

PDOException: SQLSTATE[HY000]: 一般错误: 1366 不正确的字符串值: '\xF0\x9D\x84\x8E i...' 列 'body_value' 在第 1 行: INSERT INTO

我有一个???? 字符,在mb_detect_encoding 声称是UTF-8 编码的字符串中。 我尝试将此字符串插入到 mysql 表中,该表定义为(除其他外)DEFAULT CHARSET=utf8

编辑: Drupal 总是使用 SET NAMES utf8 和可选的 COLLATE(至少在与 MySQL 通信时)。

编辑 2: 一些看起来相关的更多细节。我从 PostgreSQL 数据库中获取一些文本。我将它粘贴到一个对象上,使用 mb_detect_encoding 来验证它是 UTF-8,然后使用 node_save 将对象保存到数据库中。因此,虽然有触发导入的 HTTP 请求,但数据并非来自浏览器。

编辑 3: 数据在两个表上非规范化:

SELECT character_set_name FROM information_schema.COLUMNSC WHERE table_schema = "[database]" AND table_name IN ("field_data_body", "field_revision_body") AND column_name = "body_value";

>+--------------------+
| character_set_name |
+--------------------+
| utf8               |
| utf8               |
+--------------------+

编辑 4: 这个角色有可能是“新人”吗?我对the relationship between unicode and UTF-8 有点模糊,但这个wikipedia article 暗示这个字符是最近才标准化的。

我不明白为什么会因为“字符串值不正确”而失败。

【问题讨论】:

该表上的字段是否使用 UTF-8 字符集定义? UTF-8 Database Problem的可能重复 SELECT character_set_name FROM information_schema.`COLUMNS` C WHERE table_schema = "db_name" AND table_name = "table_name" AND column_name = "column_name"; 提供了什么 MySQL 的 utf8 只是 BMP。它的utf8mb4对应外界的UTF-8(包含4字节字符)。 该错误是由于试图将 4 个字节塞进一个无法处理的字符集(即 MySQL 的“utf8”)。 【参考方案1】:

? (U+1D10E) 是在 BMP(基本多语言平面)之外(在 U+FFFF 之上)发现的字符 Unicode,因此不能用 UTF-8 以 3 个字节表示。 MySQL charset utf8 仅接受 UTF-8 字符,如果它们可以用 3 个字节表示。如果需要将其存储在 MySQL 中,则需要使用 MySQL charset utf8mb4。您需要 MySQL 5.5.3 或更高版本。您可以使用 ALTER TABLE 更改字符集,没有太大问题;因为它需要更多空间来存储字符,所以出现了一些可能需要您减小字符串大小的问题。见http://dev.mysql.com/doc/refman/5.5/en/charset-unicode-upgrading.html

【讨论】:

太好了,非常感谢。在进行这种特定类型的保存时更改表并执行“S​​ET NAMES”,为我解决了这个问题。 旧版本的 MySQL 有解决方案吗? 如果他可以更改数据库,他肯定可以比使用 Postgresql 更轻松地升级 MySQL。 @JeromeJ - 5.5.3 是引入 utf8mb4 的时候。抱歉,没有 text 方法来存储 4 字节 UTF-8(非 BMP)字符。 @RickJames 我想我确实在保存字符串之前用它们的 HTML 字符替换了所有 4 字节的 UTF-8。【参考方案2】:

要解决这个问题,首先将数据库字段更改为 utf8m4b 字符集。例如:

ALTER TABLE `tb_name` CHANGE `field_name` `field_name` VARCHAR(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL; 

然后在您的数据库连接中,将 driver_options 设置为 utf8mb4。例如,如果您使用 PDO

$db = new PDO('mysql:host=localhost;dbname=testdb;charset=utf8mb4', 'username', 'password');

或在 zend 框架 1.2 中

$dbParam = array('host' => 'localhost', 'username' => 'db_user_name',
            'password' => 'password', 'dbname' => 'db_name',
            'driver_options' => array(
                '1002' => "SET NAMES 'utf8mb4'",
                '12'    => 0 //this is not necessary
            )
        );

【讨论】:

这里的 1002 和 12 代表什么? 它们是PDO mysql中driver_options的选项。您可以参考这里了解更多详情php.net/manual/en/ref.pdo-mysql.php PDO::MYSQL_ATTR_INIT_COMMAND (1002) 和 PDO::FETCH_KEY_PAIR (12)。与问题相关的 FETCH_KEY_PAIR 是否必要?【参考方案3】:【参考方案4】:

我修复了错误: SQLSTATE[HY000]:一般错误:1366 字符串值不正确...... 用这个方法:

我使用 utf8mb4_unicode_ci 作为数据库 为所有表设置 utf8mb4_unicode_ci

为列设置长博客数据类型(不是文本,长文本......您需要大数据类型来存储 4 个字节的内容)

现在好了。 如果你使用laravel,继续编辑config/database.php

'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',

如果您使用函数 strtolower,请将其替换为 mb_strtolower 注意:你必须把<meta charset="utf-8">放在你的head标签上

【讨论】:

以上是关于将 UTF-8 编码的字符串插入 UTF-8 编码的 mysql 表失败​​,并显示“字符串值不正确”的主要内容,如果未能解决你的问题,请参考以下文章

将 wstring 转换为 UTF-8 编码的字符串

字符串转换成utf-8编码

编码与解码

将 UTF-8 编码的 NSData 转换为 NSString

刨根究底字符编码之十一——UTF-8编码方式与字节序标记

将字符串的编码格式转换为utf-8