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

标签 mysql unicode utf-8 character-encoding mariadb

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

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


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


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

最佳答案

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

您列出了CHARACTER SET故障的五种主要情况。

最佳实践

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

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

在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。)
HTML开头的<meta charset=UTF-8>
存储的例程获取当前的字符集/排序规则。他们可能需要重建。


UTF-8 all the way through

More details for computer languages(及其后续部分)

测试数据

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

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


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


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


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

截断的文本(Se表示Señor):


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


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

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


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


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


SET NAMES的连接(或SELECT)不是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。


Mojibake(SeñorSeñor):
(此讨论也适用于Double Encoding,它不一定是可见的。)


要存储的字节需要UTF-8编码。解决这个问题。
INSERTingSELECTing文本时的连接需要指定utf8或utf8mb4。解决这个问题。
该列需要声明为CHARACTER SET utf8(或utf8mb4)。解决这个问题。
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种不同修复;请谨慎选择):http://mysql.rjweb.org/doc.php/charcoll#fixes_for_various_cases

关于mysql - UTF-8字符有问题;我看到的不是我存储的,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40073387/

相关文章:

php - 如何将一栏的内容复制到另一栏?

python - 使用正则表达式从字符串中仅提取 Unicode 字符

java - 输入流特殊字符UTF-8

java - 字节顺序标记搞砸了 Java 中的文件读取

MySQL 查询未给出预期结果!= 不工作

mysql - 在 MySQL 中搜索 2 个日期之间的条目

MySQL 从重复行中查找 MIN()

python 2.7 string.join() 与 unicode

java - 打印编码的 unicode 字符

c++ - 通过检查输入字节确定输入编码