将UTF-8编码的字符串插入到UTF-8编码的MySQL表中失败,出现“不正确的字符串值”错误。

14

将UTF-8编码的字符串插入到UTF-8编码的表中会导致错误的字符串值。

PDOException:SQLSTATE [HY000]:一般性错误:1366列'body_value'的行1处存在不正确的字符串值:INSERT INTO

我有一个包含字符的字符串,mb_detect_encoding声称其为UTF-8编码。

我尝试将该字符串插入到MySQL表中,该表被定义为(除其他外)DEFAULT CHARSET = utf8

编辑:Drupal始终使用可选的COLLATE进行SET NAMES utf8(至少在与MySQL交互时)。

编辑2:似乎还有一些相关细节。我从PostgreSQL数据库中获取了一些文本。我将其放到一个对象上,使用mb_detect_encoding验证它是否为UTF-8,并使用node_save将对象持久化到数据库中。因此,虽然有一个HTTP请求触发了导入,但数据并不来自浏览器。

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

  

SELECT character_set_name FROM information_schema.COLUMNS C WHERE table_schema =“[database]”AND table_name IN(“field_data_body”,“field_revision_body”)AND column_name =“body_value”;

<code>>+--------------------+
| character_set_name |
+--------------------+
| utf8               |
| utf8               |
+--------------------+
</code>

编辑 4:这个字符可能是“to new”(全新的)吗?我对Unicode和UTF-8之间的关系有些模糊,但是这篇StackOverflow问题 和这篇维基百科文章都暗示着这个字符是最近才被标准化。

我不明白为什么会出现“Incorrect string value”的错误。


该表的字段是否定义为UTF-8字符集? - Mike Brant
可能是UTF-8数据库问题的重复。 - Dan Grossman
1
SELECT character_set_name FROM information_schema.\COLUMNS` C WHERE table_schema = "db_name" AND table_name = "table_name" AND column_name = "column_name";` 给出什么结果? - Esailija
MySQL的 utf8 只包含BMP字符集。而它的 utf8mb4 对应于国际标准的 UTF-8(并包括4字节字符)。 - Rick James
错误是由于试图将4个字节塞入一个不能处理它的字符集(即MySQL的“utf8”)中引起的。 - Rick James
4个回答

25

(U + 1D10E)是一个超出BMP(Basic Multilingual Plane,即U + FFFF以上)的字符Unicode,因此不能用3个字节的UTF-8表示。MySQL 字符集utf8仅接受可用3个字节表示的UTF-8字符。如果您需要将其存储在MySQL中,则需要使用MySQL字符集utf8mb4。 您需要MySQL 5.5.3或更高版本。 您可以使用ALTER TABLE轻松更改字符集;由于需要更多空间来存储字符,可能会出现一些问题,这可能需要您减少字符串大小。请参见http:// dev.mysql.com / doc / refman / 5.5 / en / charset-unicode-upgrading.html


非常好,非常感谢。对于我来说,修改表并在进行此特定类型的保存时执行“SET NAMES”解决了问题。 - Letharion
旧版本的MySQL有解决方案吗? - jeromej
1
如果他能更改数据库,那么使用MySQL升级肯定比使用Postgresql容易得多。 - prosfilaes
@JeromeJ - 5.5.3 是引入 utf8mb4 的版本。很抱歉,没有一种“文本”方式可以存储4字节的UTF-8(非BMP)字符。 - Rick James
@RickJames 我想我最终确实是在保存字符串之前用它们的HTML字符替换了所有4字节UTF-8。 - jeromej

8

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

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 Framework 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代表什么? - danronmoon
它们是PDO mysql中driver_options的选项。您可以在此处查看更多详细信息https://www.php.net/manual/en/ref.pdo-mysql.php - ytdm
PDO::MYSQL_ATTR_INIT_COMMAND(1002)和PDO::FETCH_KEY_PAIR(12)。与问题相关,需要使用FETCH_KEY_PAIR吗? - danronmoon

4
在您的PDO连接中,设置字符集。
new PDO('mysql:host=localhost;dbname=the_db;charset=utf8mb4', $user, $password);

我的关于“SET NAMES”的问题编辑是否会改变这个问题?我无法想象一个被广泛使用的框架会犯这样的错误。 - Letharion
很奇怪,你在插入什么/在哪里插入?可以尝试在头部添加utf8:<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> - wesside
我的<HEAD>标签中包含<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />,但浏览器没有传输任何数据。(我在问题中再次更新了更多数据) - Letharion
不,它必须是utf8mb4,而不仅仅是utf8。(我编辑了答案。) - Rick James

3

我已经修复了错误: SQLSTATE[HY000]:General error: 1366 不正确的字符串值..... 使用以下方法:

我在数据库中使用 utf8mb4_unicode_ci database 将所有表格都设为 utf8mb4_unicode_ci tables

将列设置为 longblog 数据类型(不是 text、longtext 等。你需要大的数据类型来存储你内容的 4 个字节) fields

现在可以了。 如果你使用 Laravel,继续编辑 config/database.php

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

laravel

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接