2015-05-14 74 views
0

用java程序在MySQL client.For示例执行的SQL命令期间嘲笑字符集转换:如何使用java代码来模拟mysql charset转换?

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/ | 
+--------------------------+---------------------------------------+ 
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'; 
mysql> select data,hex(data) from t4; 
+------+-----------+ 
| data | hex(data) | 
+------+-----------+ 
| ?? | 3F3F  | 

从MySQL文件(https://dev.mysql.com/doc/refman/5.0/en/charset-connection.html),似乎数据首先从我的操作系统的字符集(UTF8)到客户端转换(gbk),然后从gbk(客户端)到latin1(连接)。因此,基于上述理解,我编写了一个java测试程序员来模拟这种看不见的转换。请看下图:

/** 
* os utf-8 
* character_set_client gbk 
* character_set_connection latin1 
* field latin1 
* 
* @throws UnsupportedEncodingException 
*/ 
@Test 
public void test_os_utf8_to_client_gbk_to_connection_latin1() throws UnsupportedEncodingException{ 
    String emoji = ""; 
    String receivedStr = new String(emoji.getBytes("utf-8"),"gbk"); //os(utf-8)-->client(gbk) 
    System.out.println(receivedStr);//馃榾 
    String convertedStr = new String(receivedStr.getBytes("latin1"),"latin1"); //client(gbk) --> connection(latin1) 
    System.out.println(convertedStr);//?? 
    printHexString(convertedStr.getBytes("latin1")); //3f 3f 

}

上面的代码可以得到相同的结果,实际操作MySQL。 我想知道这个模拟背后的原理是正确的还是恰到好处的?

回答

0

是一个4字节的utf8字符。在MySQL中,它需要CHARACTER SET utf8mb4,而不是utf8。 (外界称之为utf8UTF-8,但MySQL有一个区别。)

\U+1F600还表示,这将是UTF8 4个字节,因为它比FFFF更大。

馃榾可以在MySQL的utf8或utf8mb4中呈现。这是十六进制E9A683 E6A6BE

latin1对于这两种情况都不是很好的MySQL CHARACTER SET。它可以被使用(虽然不是你尝试的方式),但只是因为latin1未能检查字节的有效性。 全部 8位值在latin1中有一些含义。但是,您可能会得到😀的表情符号或馃榾这对中文字符。

您的六个character%的混合设置是简单的要求麻烦。尝试从默认值开始,然后使用SET NAMES正好更改其中的三个。 3是_client,_connection_results

问号,是因为:

  • 你有UTF8编码的数据(好)
  • SET NAMES latin1已生效(默认,但错)
  • 列被宣布CHARACTER SET latin1(默认值,但错误)

无法从表格中检索数据。

做正确的事情,

  • UTF8编码的数据(好)
  • 建立utf8mb4从客户机到服务器的连接时
  • 检查柱子(S)和/或表默认是CHARACTER SET utf8mb4
  • 如果您在网页上显示,<meta...charset=utf-8>应该靠近顶部。

(两个那些指定utf8mb4因为他们所谈论到MySQL;另外两个是比较通用的utf8。)

你提到gbk。如果客户端中的字节编码为“gbk”,则继续本段。 (我怀疑它们不是,因为Java喜欢处理Unicode,因此是UTF8)。如果已建立SET NAMES gbk,则可以在客户端中有gbk字节。这意味着gbk编码的字节将被转码到/从表的CHARACTER SET。注:latin1不可以容纳汉字,也不是表情符号,所以CHARACER SET latin1不是有效的对象。 CHARACTER SET gbkutf8mb4应该可以工作。 (此外,我怀疑gbk不能保存表情符号,所以utf8mb4可能是唯一合理的选项,并且SET NAMES gbk可能不起作用。)

+0

感谢您的建议!但我不要求一个好的做法。我想知道mysql charset背后的机制。所以我必须在这个过程中设置差异字符集并观察结果来验证我的假设。因为我不是很熟悉C++,现在通过检查mysql源代码来验证它对我来说有点困难。所以我用java代码来模拟这个。其实我只是想知道mysql charset转换的机制,就是这个java代码是否可以表达真正的mysql操作,或者恰好碰巧得到相同的结果 – zhuguowei

+0

我花了好几年的时间学习了“机制”。我所知道的大部分内容都在[我的博客](http://mysql.rjweb.org/doc.php/charcoll)中。这个答案中给你的是一个缩写,专门针对你提到的内容。由于有关于“移动部件”的内容,实验起来相当具有挑战性。 –