Mysql latin1也支持emoji字符的错觉分析

起初发现了如下的现象:

mysql> show variables like ‘character%‘;
+--------------------------+---------------------------------------+
| Variable_name            | Value                                 |
+--------------------------+---------------------------------------+
| character_set_client     | latin1                                |
| character_set_connection | latin1                                |
| character_set_database   | latin1                                |
| character_set_filesystem | binary                                |
| character_set_results    | latin1                                |
| character_set_server     | utf8mb4                               |
| character_set_system     | utf8                                  |
| character_sets_dir       | /opt/mysql/server-5.6/share/charsets/ |
+--------------------------+---------------------------------------+
mysql> show create table t4\G
*************************** 1. row ***************************
   Table: t4
Create Table: CREATE TABLE `t4` (
  `data` varchar(100) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1
mysql> insert into t4 select ‘\U+1F600‘;

觉得很奇怪怎么latin1也支持emoji字符了呢?不是只有utf8mb4才支持吗? 于是在StackOverFlow上提问,一个网友的回答觉得有道理,回答如下:

I think you saved into and retrieved from the database a string of bytes that is interpreted by the terminal as an Unicode character. Check the output of SELECT LENGTH(data), CHAR_LENGTH(data) FROM t4 to see what‘s happening. They should return different values for multi-byte characters and the same value forlatin1. –  axiac 19 hours ago

在加上无意中看到了一篇博客, 其中说道:

抛一个问题,latin1字符集的表,用户写入和读取汉字是否有问题?答案是只要合理设置,没有问题。假设SecureCRT为UTF8,character_set_client和表字符集均设置为latin1,参考第3节的分析,那么用户读取和写入数据的过程中,并不涉及字符集编码转换的问题,将UTF8的汉字字符转为二进制流写入database,提取出来后,secureCRT再将对应的二进制解码为对应的汉字,所以不影响用户的使用。

于是现在觉得上述现象很正常。

因为操作系统默认的字符集为utf8(LANG=en_US.UTF-8), 而client、connection、database均为latin1, 于是这一路(从终端界面执行insert到保存数据到表中)都没有编码转换,直接传输的是utf8编码后的二进制流。

怎么验证上述结论呢? 于是决定修改中间环节的字符集,看会发生什么?

    mysql> set names gbk;
    mysql> show variables like ‘character%‘;
    +--------------------------+---------------------------------------+
    | Variable_name            | Value                                 |
    +--------------------------+---------------------------------------+
    | character_set_client     | gbk                                   |
    | character_set_connection | gbk                                   |
    | character_set_database   | latin1                                |
    | character_set_filesystem | binary                                |
    | character_set_results    | gbk                                   |
    | character_set_server     | utf8mb4                               |
    | character_set_system     | utf8                                  |
    | character_sets_dir       | /opt/mysql/server-5.6/share/charsets/ |
    +--------------------------+---------------------------------------+
    mysql> insert into t4 select ‘\U+1F600‘;
    ERROR 1366 (HY000): Incorrect string value: ‘\xF0\x9F\x98\x80‘ for column ‘data‘ at row 1

    分析:

    现在操作系统是utf8, client、connection是gbk, 字段是latin1, 因为一开始是utf8二进制流,且client和connection均为gbk,无需转码,故只在最后当保存到表字段中时需要由utf8转为latin1,由于latin1不能解码该utf8二进制流故导致了上述报错。

    若将字符集不一致的情况再往前挪一步会怎样呢? 如下所示:

    mysql> set character_set_connection = latin1;
    mysql> show variables like ‘character%‘;
    +--------------------------+---------------------------------------+
    | Variable_name            | Value                                 |
    +--------------------------+---------------------------------------+
    | character_set_client     | gbk                                   |
    | character_set_connection | latin1                                |
    | character_set_database   | latin1                                |
    | character_set_filesystem | binary                                |
    | character_set_results    | gbk                                   |
    | character_set_server     | utf8mb4                               |
    | character_set_system     | utf8                                  |
    | character_sets_dir       | /opt/mysql/server-5.6/share/charsets/ |
    +--------------------------+---------------------------------------+

    现在client和connection就不一致了,就是说需要先将utf8-->gbk-->latin1, 那么现在能成功插入emoji字符吗?

    mysql> insert into t4 select ‘\U+1F600‘;

    可以插入,查询结果如下:

    mysql> select data,hex(data) from t4;
    +------+-----------+
    | data | hex(data) |
    +------+-----------+
    | ??   | 3F3F      |
    +------+-----------+

    似乎在utf8-->gbk的过程中,将utf8编码后的二进制流(f0 9f 98 80)解码成了‘??’,而‘??’能被latin1成功解析。但如何通过java程序模拟上述的转换呢?

    试了下面的代码 但未能成功再现。

    即将utf8转为gbk时未能得到‘??’,而是‘馃榾’。不知何故?

    那假如将上例中的client与connection交换一下位置呢,如下所示:

    mysql> show variables like ‘character%‘;
    +--------------------------+---------------------------------------+
    | Variable_name            | Value                                 |
    +--------------------------+---------------------------------------+
    | character_set_client     | latin1                                |
    | character_set_connection | gbk                                   |
    | character_set_database   | latin1                                |
    | character_set_filesystem | binary                                |
    | character_set_results    | gbk                                   |
    | character_set_server     | utf8mb4                               |
    | character_set_system     | utf8                                  |
    | character_sets_dir       | /opt/mysql/server-5.6/share/charsets/ |
    +--------------------------+---------------------------------------+

    现在的转化流变成这样了:utf8-->latin1-->gbk-->latin1, 从之前的经验似乎可以预测进行第一步转化时就应该报错(Incorrect string value: ‘\xF0\x9F\x98\x80‘ for column ‘data‘ at row 1),但实际情况是:

    mysql> insert into t4 select ‘\U+1F600‘;
    Query OK, 1 row affected (0.01 sec)
    mysql> select data,hex(data) from t4;
    +------+-----------+
    | data | hex(data) |
    +------+-----------+
    | ??   | 3F3F      |
    | ???? | 3F3F3F3F  |
    +------+-----------+

    并未报错仍能成功插入, 似乎只要不是最后一步往表里插入记录就不会报错,但这次变成4个问号了。

    这次用java程序模拟的结果如下所示:

    注:

    1. 为什么用Cp1252表示latin1?

    主要是参考了该表格:

    Table 5.3 MySQL to Java Encoding Name Translations

    时间: 2024-12-14 07:14:40

    Mysql latin1也支持emoji字符的错觉分析的相关文章

    mysql:设置字符集utf8mb4 支持emoji字符

    为什么要把数据库的字符集设置成utf8mb4呢?以前一直用的都是utf8啊? 答案在这里:utf8适用于不使用移动设备的互联网交互,utf8mb4适用于当前的移动设备互联网开发,因为移动设备中常常会有表情符号(emoji)的存储,它占用4个字节的存储空间,而utf8是3个字节,这样,用3个字节去存储4个字节的东西,很明显是存不下的,会报错,所以要用utf8mb4,并且utf8mb4是兼容utf8的,那么,就没有理由不用utf8mb4字符集了. 介绍设置MySQL的字符集: 1 在ubuntu安装

    让Java后台MySQL数据库能够支持emoji表情的方法

    千万不要相信 设置Java的数据库连接,将characterEncoding去掉,然后保证mysql connector版本高于5.1.13,就OK了 更不要相信 navicat查数据库看到记录是“?” 上面 的"?"在前端展示时是正常的 这样做的后果只能是自己环境调试过滤了,但是部署到例如阿里云服务器上后直接程序莫名其妙的查询不出结果了 如果表情不是必须的,建议替换掉再插入数据库

    如何让应用程序支持emoji

    什么是emoji?就是这些表情和符号: ?????????????????? 自iPhone从iOS 5在输入法中开始支持emoji以来,这些表情符号迅速风靡世界.但是很多Web网站竟然还不!支!持!!! 那怎么才能支持emoji呢?其实代码一行都不用改,因为emoji符号实际上是文本,并不是图片,它们仅仅显示为图片而已.而且,emoji符号已经被标准化并编码到最新的Unicode标准中了,所以,要支持emoji,只需要底层软件系统支持就可以了. 服务器端要正确存储emoji符号,只需要确保We

    mysql支持emoji表情上传

    背景: 由于需要实现emoji表情评论的功能,所以数据库需要支持emoji表情的存储 mysql的utf8编码的一个字符最多3个字节,但是一个emoji表情为4个字节,所以utf8不支持存储emoji表情.但是utf8的超集utf8mb4一个字符最多能有4字节,所以能支持emoji表情的存储.但是在mysql5.5之前并不支持utf8mb4编码,所以mysql一定要在5.5及以上 1.首先修改mysql配置文件 1 [mysqld] 2 character-set-client-handshak

    nodejs项目mysql使用sequelize支持存储emoji

    nodejs项目mysql使用sequelize支持存储emoji 本篇主要记录nodejs项目阿里云mysql如何支持存储emoji表情. 因由 最近项目遇到用户在文本输入emoji进行存储的时候导致存储失败的问题.经本地调试发现emoji表情在存储时转成的四个字节(\xF0\x9F\x90\xAC)导致sequelize报错,Unhandled rejection SequelizeDatabaseError.由于数据库使用的是utf8字符集utf8_general_ci,这个校对规则(co

    MySQL utf8mb4 字符集:支持 emoji 表情符号

    ㈠ 序言 为了应对无线互联网的机遇和挑战.避免 emoji 表情符号带来的问题. 涉及无线相关的 MySQL 数据库建议都提前采用 utf8mb4 字符集 这必须要作为移动互联网行业的一个技术选型的要点 ㈡ 限制 需要 >= MySQL 5.5.3版本.从库也必须是5.5的了.低版本不支持这个字符集.复制报错 ㈢ 简要步骤 以下是一个未升级到 5.5.3 的配置步骤: ① 备份数据库 ② 升级 MySQL Server 到 v5.5.3+ ③ 修改 database.table和column字符

    让MySQL支持Emoji表情 mysql 5.6

    让MySQL支持Emoji表情,涉及无线相关的 MySQL 数据库建议都提前采用 utf8mb4 字符集. mysql 版本 5.6 1 解决方案:将Mysql的编码从utf8转换成utf8mb4. 需要 >= MySQL 5.5.3版本.从库也必须是5.5的了.低版本不支持这个字符集.复制报错 2 my.cnf 文件添加 [mysqld]  character-set-server = utf8mb4  collation-server = utf8mb4_unicode_ci  init_c

    mysql支持emoji表情存取的解决方案

    让mysql支持emoji表情存取的解决方案 APP移动端无需做任何修改,服务端修改即可. 第一步:修改jdbcUrl: conn.url=jdbc:mysql://127.0.0.1:3306/eyes?zeroDateTimeBehavior=convertToNull&autoReconnect=true 第二步:mysql驱动包: mysql-connector-java-5.1.24.jar 第三步:数据库版本检查: select version() #查看mysql版本 5.6.23

    mysql支持emoji表情

    让MySql支持Emoji表情 解决方案:将Mysql的编码从utf8转换成utf8mb4. 需要 >= MySQL 5.5.3版本.(经检测5.5.29的也可以)低版本不支持这个字符集.复制报错 停止MySQL Server服务 修改 my.cnf或者mysql.ini [client] default-character-set = utf8mb4 [mysql] default-character-set = utf8mb4 [mysqld] character-set-client-ha