UTF-8 字符问题;我看到的不是我存储的

Trouble with UTF-8 characters; what I see is not what I stored

提问人:Rick James 提问时间:7/14/2016 最后编辑:Peter MortensenRick James 更新时间:6/14/2023 访问量:102694

问:

我尝试使用UTF-8并遇到了麻烦。

我尝试了很多方法;以下是我得到的结果:

  • ????而不是亚洲字符。即使是欧洲文本,我也得到了.Se?orSeñor
  • 奇怪的胡言乱语(Mojibake?),例如 or for .Señor新浪新闻新浪新闻
  • 黑色钻石,如硒或。
  • 最后,我遇到了数据丢失或至少被截断的情况:for .SeSeñor
  • 即使我让文本看起来正确,它也没有正确排序

我做错了什么?如何修复代码?我可以恢复数据吗,如果可以,如何恢复?

MySQL Unicode UTF-8 字符编码 MariaDB

评论


答:

194赞 Rick James 7/14/2016 #1

这个问题困扰着本网站的参与者以及许多其他人。

您列出了五种主要的麻烦情况。CHARACTER SET

最佳实践

展望未来,最好使用 和 .(管道中有一个较新版本的 Unicode 排序规则。CHARACTER SET utf8mb4COLLATION utf8mb4_unicode_520_ci

utf8mb4是 的超集,因为它处理 4 字节的 utf8 代码,这是 Emoji 和一些中文需要的。utf8

在MySQL之外,“UTF-8”指的是所有大小的编码,因此实际上与MySQL的相同,而不是。utf8mb4utf8

在下文中,我将尝试使用这些拼写和大小写来区分MySQL内部和外部。

您应该采取的措施概述

  • 将编辑器等设置为 UTF-8。
  • HTML 表单应以 .<form accept-charset="UTF-8">
  • 将字节编码为 UTF-8。
  • 将 UTF-8 建立为客户端中使用的编码。
  • 声明列/表(使用 .CHARACTER SET utf8mb4SHOW CREATE TABLE
  • <meta charset=UTF-8>在 HTML 的开头
  • 存储例程获取当前字符集/排序规则。他们可能需要重建。

UTF-8 贯穿始终

计算机语言(及其以下部分)的更多详细信息

测试数据

使用工具或 不可信地查看数据。 太多这样的客户端,尤其是浏览器,试图补偿不正确的编码,并显示正确的文本,即使数据库被破坏了。 因此,选择一个包含一些非英语文本的表格和列,然后做SELECT

SELECT col, HEX(col) FROM tbl WHERE ...

正确存储的 UTF-8 的十六进制将是

  • 对于空白区域(任何语言):20
  • 英语:、、或4x5x6x7x
  • 对于西欧的大部分地区,重音字母应该是Cxyy
  • 西里尔文、希伯来文和波斯语/阿拉伯语:Dxyy
  • 亚洲大部分地区:Exyyzz
  • 表情符号和一些中文:F0yyzzww
  • 更多详情

所见问题的具体原因和解决方法

截断文本 ( for):SeSeñor

  • 要存储的字节未编码为 utf8mb4。解决此问题。
  • 此外,请检查读取期间的连接是否为 UTF-8。

带问号的黑钻 ( for ); 存在以下情况之一:Se�orSeñor

案例 1(原始字节不是 UTF-8):

  • 要存储的字节未编码为 utf8。解决此问题。
  • 的连接(或)不是 utf8/utf8mb4。解决此问题。SET NAMESINSERTSELECT
  • 此外,请检查数据库中的列是否为 (或 utf8mb4)。CHARACTER SET utf8

案例 2(原始字节 UTF-8):

  • 的连接(或)不是 utf8/utf8mb4。解决此问题。SET NAMESSELECT
  • 此外,请检查数据库中的列是否为 (或 utf8mb4)。CHARACTER SET utf8

仅当浏览器设置为 时才会出现黑菱形。<meta charset=UTF-8>

号(常规问号,非黑菱形)(用于):Se?orSeñor

  • 要存储的字节未编码为 utf8/utf8mb4。解决此问题。
  • 数据库中的列不是 (或 utf8mb4)。解决此问题。(使用 .)CHARACTER SET utf8SHOW CREATE TABLE
  • 此外,请检查读取期间的连接是否为 UTF-8。

Mojibake ( 用于 ): (此讨论也适用于双重编码,它不一定可见。SeñorSeñor

  • 要存储的字节需要采用 UTF-8 编码。解决此问题。
  • 连接 when 和 text 需要指定 utf8 或 utf8mb4。解决此问题。INSERTingSELECTing
  • 需要声明该列(或 utf8mb4)。解决此问题。CHARACTER SET utf8
  • HTML 应以 .<meta charset=UTF-8>

如果数据看起来正确,但无法正确排序,则 要么你选错了排序规则, 或者没有适合您需要的排序规则, 或者你有双重编码

可以通过执行上述操作来确认双重编码SELECT .. HEX ..

é should come back C3A9, but instead shows C383C2A9
The Emoji 👽 should come back F09F91BD, but comes back C3B0C5B8E28098C2BD

也就是说,十六进制的长度大约是应有的两倍。 这是由于从 latin1(或其他)转换为 utf8,然后处理这些 字节,就好像它们是 latin1 并重复转换一样。 排序(和比较)无法正常工作,因为它是,例如, 排序,就好像字符串是 .Señor

在可能的情况下修复数据

对于截断问号,数据将丢失。

对于Mojibake /双重编码,...

对于黑钻,...

此处列出了修复程序:针对 5 种不同情况的 5 种不同修复程序;请仔细挑选

相关新闻: 非法混合排序规则

评论

0赞 david_adler 1/16/2018
如果客户端、数据库和表都在,我似乎能够很好地存储表情符号。一些博客还建议在mysqld中设置和。我真的需要更改服务器设置有什么区别吗?utf8mb4collation-servercharacter-set-servermysqld
1赞 Rick James 1/16/2018
@david_adler - 有多种方法可以获得这些设置的效果。最好使用客户端连接参数。连接后立即执行的第二好方法。毕竟,这是在客户端中声明编码。SET NAMES utf8mb4
0赞 Rick James 5/10/2018
在MySQL 8.0(现已发布)中,默认值为和。大多数用户应该在不考虑其他字符集和排序规则的情况下使用它们。utf8mb4utf8mb4_0900_ai_ci
0赞 Rick James 6/30/2018
配置 PythonPHP 和大约 40 种其他语言的提示
0赞 Rick James 1/4/2019
另一个注意事项:如果涉及 or,则在创建时可能没有使用所需的字符集。 它;重新。FUNCTIONSTORED PROCEDUREDROPSET NAMESCREATE
2赞 SIDU 7/14/2016 #2
  1. 将代码 IDE 语言设置为 UTF-8

  2. 将 <meta charset=“utf-8”>添加到收集数据表单的网页标题中。

  3. 检查您的 MySQL 表定义如下所示:

     CREATE TABLE your_table (
       ...
     ) ENGINE=InnoDB DEFAULT CHARSET=utf8
    
  4. 如果您使用的是 PDO,请确保

    $options = array(PDO::MYSQL_ATTR_INIT_COMMAND=>'SET NAMES utf8');
    $dbL = new PDO($pdo, $user, $pass, $options);
    

如果您已经有一个存在上述问题的大型数据库,您可以尝试使用 SIDU 使用正确的字符集导出,并使用 UTF-8 导入回来。

评论

0赞 Rick James 7/14/2016
for a table 就是这样,默认设置。它可以,有时应该在列定义上覆盖它。DEFAULT CHARSET
2赞 Rick James 7/14/2016
PDO最好使用charset选项完成:(这列在我的“charcoll”文档的链接中。$db = new PDO('dblib:host=host;dbname=db;charset=UTF8', $user, $pwd);
0赞 SIDU 7/14/2016
你比我多 20K :)是的,您可以为列设置字符集。尽量不要过度使用它。最终有更多的管理时间。同样,您可以授予对 MySQL 表的特定列的访问权限。但是,除非您没有更好的选择,否则您不必使用它。
2赞 spencer7593 11/10/2017
@ppmakeitcount:不,该语句不需要重新启动MySQL即可生效。但是,更改数据库的默认字符集不会影响数据库中当前的任何表;它只对新表有影响,例如 未为表指定默认字符集;这时,数据库默认字符集就开始发挥作用了。(同样,更改表的默认字符集不会影响表中已有的列;仅当未指定列字符集时,它才会对添加到表中的列产生影响。ALTER DATABASECREATE TABLE
1赞 Rick James 11/9/2021
@bballdave025 - 谢谢。我花了很长时间——首先发现了所有不同的案例,然后弄清楚是什么导致了每个案例,然后花了更多的时间来弄清楚如何简洁地解释它们。
-5赞 paul 8/4/2016 #3

根据服务器的设置方式,您必须相应地更改编码。从你所说的 utf8 应该效果最好。但是,如果您遇到奇怪的字符,将网页编码更改为 ANSI 可能会有所帮助。

这在我设置 PHP MySQLi 时帮助了我。这可能有助于您了解更多: Notepad++ 中的 ANSI 到 UTF-8

评论

1赞 Rick James 8/4/2016
记事本的可能最接近MySQL的。该链接中的0x93可能来自Word之类的地方。您可以转换为 utf8 (hex )或告诉MySQL数据是,并希望您不要在其他地方绊倒。ANSIlatin1E2809Clatin1
14赞 castro_pereira 3/25/2019 #4

在服务器迁移后,我的两个项目也遇到了类似的问题。在搜索并尝试了很多解决方案之后,我遇到了这个:

mysqli_set_charset($con,"utf8mb4");

将这一行添加到我的配置文件后,一切正常!

我为 MySQLi 找到了这个解决方案——PHP mysqli set_charset() 函数——当我想要解决 HTML 查询中的插入时。

评论

1赞 Rick James 3/26/2019
是的,这是可能导致字符集问题的几件事之一。注意:该语法对 PHP 有效,对其他应用程序语言无效,并且仅当使用 、而不是 .mysqliPDO
5赞 Ashish Bhatt 12/20/2019 #5

我也在寻找同样的问题。我花了将近一个月的时间才找到合适的解决方案。

首先,您必须将所有最近的 CHARACTER 和 COLLATION 更新数据库为 utf8mb4 或至少支持 UTF-8 数据。

对于 Java:

在建立 JDBC 连接时,将其添加到连接 URL useUnicode=yes&characterEncoding=UTF-8 作为参数,它将起作用。

对于 Python:

在查询数据库之前,请尝试在游标上强制执行此操作

cursor.execute("SET NAMES utf8mb4")
cursor.execute("SET CHARACTER SET utf8mb4")
cursor.execute("SET character_set_connection=utf8mb4")

如果它不起作用,请愉快地寻找正确的解决方案。

评论

0赞 Rick James 12/20/2019
1个月?那真是太快了。我花了一年多的时间来制定这个问答。 不是 Python 的“正确”方式;请参阅 mysql.rjweb.org/doc.php/charcoll#python 该博客的其他地方讨论了许多其他语言。SETs
1赞 Ashish Bhatt 7/28/2020
@RickJames 但是 Mysql-Python 低于 1.2.4 时存在这个问题,因此这些语句基本上是一种解决方法。SET
1赞 Peter Mortensen 4/13/2022
“cursor.execute”附近的内容应该如何格式化?每条线都有单独的行?还是别的什么?“*”是有文字的还是斜体格式?