最近在做MySQL数据导出到HBase中的任务,刚开始以为很简单,但后来发现坑比较多。主要是由于历史原因存在着不同的字符集格式。为了能为后面数据分析提供基础,需要对导出的数据统一编码成UTF-8。
采用jdbc方式读取数据,连接字符串设置编码格式utf-8,查询数据前首先需要执行下executeQuery("set names utf8"); 对于一次查询请求,信息输入路径:client→connection→server;信息输出路径:server→connection→results。换句话说,每个路径要经过3次改变字符集编码。以出现乱码的输出为例,server里utf8的数据,传入connection转为latin1,传入 results转为latin1,utf-8页面又把results转过来。如果两种字符集不兼容,比如latin1和utf8,转化过程就为不可逆的, 破坏性的。
我所接触到的遗留库主要是latin1和utf-8两种字符集的, 需要根据情况进行转码。既可以根据表的TABLE_COLLATION来断定(如select TABLE_SCHEMA, TABLE_NAME, TABLE_COLLATION, ENGINE from tables where TABLE_SCHEMA = "haitao" and table_name = "haitao_user_biz_deal_withdrawals), 如果是utf8_general_ci 的无需处理,如果是latin1_swedish_ci,则按照如下方式来处理:
sql 的latin1 不等于标准的latin1(iso-8859-1) 和cp1252,比iso-8859-1多了0x80-0x9f字符,比cp1252多了0x81,0x8d,0x8f,0x90,0x9d 一共5个字符。
这样在Java中,如果使用标准的iso-8859-1或者cp1252解码可能出现乱码。
s.getBytes("iso-8859-1") 或者 s.getBytes("cp1252");
写了一段代码来解决这个问题
private String convertCharset(String s){
if(s!=null){
try {
int length = s.length();
byte[] buffer = new byte[length];
//0x81 to Unicode 0x0081, 0x8d to 0x008d, 0x8f to 0x008f, 0x90 to 0x0090, and 0x9d to 0x009d.
for(int i=0;i<length;++i){
char c = s.charAt(i);
if(c==0x0081){
buffer[i]=(byte)0x81;
}
else if(c==0x008d){
buffer[i]=(byte)0x8d;
}
else if(c==0x008f){
buffer[i]=(byte)0x8f;
}
else if(c==0x0090){
buffer[i]=(byte)0x90;
}
else if(c==0x009d){
buffer[i]=(byte)0x9d;
}
else{
buffer[i] = Character.toString(c).getBytes("cp1252")[0];
}
}
String result = new String(buffer,"utf-8");
return result;
} catch (UnsupportedEncodingException e) {
logger.error("charset convert error", e);
}
}
return null;
}