【发布时间】:2015-01-09 18:04:44
【问题描述】:
当要求从包含未定义的 latin1 代码页字符的 latin1 列中读取时,JDBC 似乎插入了一个 utf8 替换字符。这种行为与 MySQL 内部函数的行为不同。
字符编码是我上周陷入的一个兔子洞,为了不产生 100 个明显的答案,我将通过几个代码示例来演示发生了什么。
Mysql:
[admin@yarekt ~]$ echo 'SELECT CONVERT(UNHEX("81") using latin1);' | mysql --init-command='set names latin1' | tail -1| hexdump -C
00000000 81 0a |..|
00000002
[admin@yarekt ~]$ echo 'SELECT CONVERT(UNHEX("81") using latin1);' | mysql --init-command='set names utf8' | tail -1| hexdump -C
00000000 c2 81 0a |...|
00000003
这很明显,并且完全按预期工作。 0x81 是未定义的 latin1 代码点。它在 UTF8 中表示为 \u0081 或在十六进制“磁盘上”表示为 c2 81。
现在怪异来自JDBC,举个时髦的例子:
@GrabConfig(systemClassLoader=true)
@Grab(group='mysql', module='mysql-connector-java', version='5.1.6')
import groovy.sql.Sql
sql = Sql.newInstance( 'jdbc:mysql://localhost/test', 'root', '', 'com.mysql.jdbc.Driver' )
sql.eachRow( 'SELECT CONVERT(UNHEX("C281") using utf8) as a;' ) { println "$it.a --" }
这个查询的输出是两个字节,如预期的那样c2 81。很容易理解这里发生的事情。 Mysql 连接默认为 UTF8。 unhexxed 列也被强制转换为 UTF8(不编码,由于源是二进制,CONVERT() 之后的数据仍然是c2 81)。
现在考虑这种情况。连接仍然是 UTF8,这是 JDBC 的默认设置。我们将 0x81 字节转换为 latin1,因此希望 mysql 将其转换为 c2 81,就像在上面的 bash 示例中所做的那样。
@GrabConfig(systemClassLoader=true)
@Grab(group='mysql', module='mysql-connector-java', version='5.1.6')
import groovy.sql.Sql
sql = Sql.newInstance( 'jdbc:mysql://localhost/test', 'root', '', 'com.mysql.jdbc.Driver' )
sql.eachRow( 'SELECT CONVERT(UNHEX("81") using latin1) as a;' ) { println "$it.a --" }
使用groovy latin1_test.groovy | hexdump -C 运行会产生这样的结果:
00000000 ef bf bd 0a |....|
00000004
ef bf bd 是一个 utf8 替换字符。 utf8 转换失败时使用的字符。
【问题讨论】:
标签: java mysql jdbc groovy utf-8