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


76

我试图使用UTF-8遇到麻烦。

我尝试了很多事情;这是我得到的结果:

  • ????而不是亚洲字符。即使是欧洲文字,我也能Se?or接受Señor
  • 奇怪的乱码(变为乱码?),如Señor新浪新闻新浪新闻
  • 黑钻石,例如Seor。
  • 最终,我陷入了数据丢失或至少被截断的情况:Sefor Señor
  • 即使我看到正确的文本,它也无法正确排序

我究竟做错了什么?我该如何修复代码?我可以恢复数据吗?

Answers:


133

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

您列出了五个主要的CHARACTER SET麻烦案例。

最佳实践

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

utf8mb4是它的超集utf8,它处理4字节utf8代码,这是表情符号和某些中文所需要的。

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

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

该做什么概述

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

UTF-8贯穿始终

有关计算机语言的更多详细信息(及其后续部分)

测试数据

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

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

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

  • 对于空格(任何语言): 20
  • 对于英语: 4x5x6x,或者7x
  • 在西欧大部分地区,带重音符号的字母应为 Cxyy
  • 西里尔文,希伯来文和波斯文/阿拉伯文: Dxyy
  • 亚洲大部分地区: Exyyzz
  • 表情符号和一些中文: F0yyzzww
  • 更多细节

出现问题的具体原因和解决方法

截断的文字(SeSeñor):

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

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

情况1(原始字节不是UTF-8):

  • 要存储的字节未编码为utf8。解决这个问题。
  • 的连接(或SET NAMES为)INSERT 所述SELECT不UTF8 / utf8mb4。解决这个问题。
  • 另外,检查数据库中的列是否为CHARACTER SET utf8(或utf8mb4)。

情况2(原始字节UTF-8):

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

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

问号(常规的,不是黑钻石)(Se?or用于Señor):

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

MojibakeSeñorfor Señor):(此讨论也适用于Double Encoding,它不一定可见。)

  • 要存储的字节需要UTF-8编码。解决这个问题。
  • INSERTingSELECTing文本的连接需要指定utf8或utf8mb4。解决这个问题。
  • 该列需要声明CHARACTER SET utf8(或utf8mb4)。解决这个问题。
  • HTML应该以开头<meta charset=UTF-8>

如果数据看起来正确,但排序不正确,则说明您选择了错误的排序规则,或者没有适合您的排序规则,或者您使用了Double Encoding

通过执行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种不同修复;请谨慎选择):http : //mysql.rjweb.org/doc.php/charcoll#fixes_for_various_cases


如果客户端,数据库和表都在utf8mb4我看来能够存储表情符号很好。一些博客建议也设置collation-servercharacter-set-servermysqld中。我真的需要更改mysqld服务器设置有什么区别吗?
david_adler '18

@david_adler-有多种方法可以使这些设置生效。最好是使用客户端连接参数。SET NAMES utf8mb4在连接之后立即执行,第二好。毕竟,这是在client中声明编码。
瑞克·詹姆斯

在MySQL 8.0(现已发布)中,默认值为utf8mb4utf8mb4_0900_ai_ci。大多数用户应在不考虑其他字符集和排序规则的情况下使用它们。
瑞克·詹姆斯


另一个注意事项:如果涉及到FUNCTION或,STORED PROCEDURE则在创建所需的字符集时可能没有使用过。 DROPSET NAMES;重新CREATE
里克·詹姆斯

6

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

mysqli_set_charset($con,"utf8");

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

当我想从html查询中解决插入问题时,我找到了针对mysqli https://www.w3schools.com/PHP/func_mysqli_set_charset.asp的解决方案

祝好运!


是的,这是可能导致字符集问题的几件事之一。注意:该语法仅对使用,而不对PHP有效,而对其他应用程序语言mysqli无效PDO
里克·詹姆斯

1

有趣的是你如何回答自己的问题:)

  1. 将您的代码IDE语言设置为UTF8

  2. 添加到您收集数据表单的网页标题中。

  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,然后使用UTF8导入回来。祝好运


8
(回答自己的问题是该论坛的功能。)多年来,我一直在努力使答案简洁而完整。
瑞克·詹姆斯

DEFAULT CHARSET一个表,只是,一个默认。在列定义中可以覆盖它,有时应该覆盖它。
瑞克·詹姆斯

2
使用charset选项可以更好地完成PDO :( $db = new PDO('dblib:host=host;dbname=db;charset=UTF8', $user, $pwd); 这在我的'charcoll'文档的链接中列出。)
Rick James

您比我高2万:)是的,您可以为列设置字符集。尽量不要过度使用它。最终增加了管理时间。同样,您可以授予对MySQL表的特定列的访问权限。但是,除非没有更好的选择,否则不必使用它。
SIDU 2016年

2
@ppmakeitcount:不,该ALTER DATABASE语句不需要重新启动MySQL即可生效。但是,更改数据库的默认字符集不会影响数据库中当前存在的任何表。它仅对新表有影响,例如CREATE TABLE不为该表指定默认字符集;这就是数据库默认字符集起作用的时候。没有指定列字符集时,它只有在添加到表中,列的效果;(类似地,改变所述表中的默认字符集不存在于表影响列已经。
spencer7593

1

我也在寻找相同的问题,我花了近一个月的时间才找到合适的解决方案。首先,您必须更新数据库,将所有最近的CHARACTER和COLLATION更新为支持utf-8数据的utf8mb4或atleast。

对于Java:

在进行JDBC连接时,将此参数添加到连接URL中,使用Unicode = yes&characterEncoding = UTF-8作为参数,它将起作用。

对于python:

在查询数据库之前,请尝试通过游标强制执行此操作* cursor.execute('SET NAMES utf8mb4') cursor.execute("SET CHARACTER SET utf8mb4") cursor.execute("SET character_set_connection=utf8mb4") *

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


1个月?那太快了。我花了一年多的时间来制定此问答。Java看起来不错。 SETs不是Python的“正确”方法;参见mysql.rjweb.org/doc.php/charcoll#python 该博客的其他地方讨论了许多其他语言。
里克·詹姆斯

@RickJames但是1.2.4以下的Mysql-Python存在此问题,因此这些SET语句基本上可以解决。
Ashish Bhatt

-3

根据服务器的设置方式,您必须相应地更改编码。您所说的utf8应该效果最好,但是如果您遇到奇怪的字符,则可以将网页编码更改为Ansi。当我设置PHP MYSQL时,这对我有所帮助,这可能有助于您了解更多/superuser/762473/ansi-to-utf-8-in-notepad


记事本ANSI可能最接近MySQL latin1。该链接中的0x93可能来自Word之类的地方。您可以转换为utf8(hex E2809C),也可以告诉MySQL数据是正确的,latin1并希望您不要在其他地方绊倒。
瑞克·詹姆斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.