编码与乱码(05)---GBK与UTF-8之间的转换--转载
原文地址:http://www.blogjava.net/pengpenglin/archive/2010/02/22/313669.html
【GBK转UTF-8】
在很多论坛、网上经常有网友问“ 为什么我使用 new String(tmp.getBytes("ISO-8859-1"), "UTF-8") 或者 new String(tmp.getBytes("ISO-8859-1"), "GBK")可以得到正确的中文,但是使用 new String(tmp.getBytes("GBK"), "UTF-8") 却不能将GBK转换成UTF-8呢?”
参考前面的【Java基础专题】编码与乱码(03)----String的toCharArray()方法测试一文,我们就知道原因了。因为如果客户端使用GBK、UTF-8编码,编码后的字节经过ISO-8859-1传输,再用原来相同的编码方式进行解码,这个过程是“无损的转换”---- 因为原始和最终的编码方式相同。
但是如果客户端使用GBK编码,到了服务器端要转换成UTF-8,或者相反的过程。想一想,字节还是那些字节,但是编码的规则变了。原来GBK编码后的4个字节要用UTF-8的每个字符3个字节的规则编码,怎么能不乱码呢?
所以从现在开始,不要再犯这种错误了。new String(tmp.getBytes("GBK"), "UTF-8") 这个过程,JVM内部是不会帮你自动对字节进行扩展以适应UTF-8的编码的。正确的方法应该是根据UTF-8的编码规则进行字节的扩充,即手动从2个字节变成3个字节,然后再转换成十六进制的UTF-8编码。
在这个专题的第一篇文章【Java基础专题】编码与乱码(01)---编码基础开头,我们就已经介绍了这个规则:
①得到每个字符的2进制GBK编码
②将该16进制的GBK编码转换成2进制的字符串(2个字节)
③分别在字符串的首位插入110,在第9位插入10,在第17位插入10三个字符串,得到3个字节
④将这3个字节分别转换成16进制编码,得到最终的UTF-8编码。
下面给出一个从网络上得到的Java转码方法,原文链接见:http://jspengxue.javaeye.com/blog/40781。下面的代码做了小小的修改
package example.encoding;
/**
* The Class CharacterEncodeConverter.
*/
public class CharacterEncodeConverter {
/**
* The main method.
*
* @param args the arguments
*/
public static void main(String[] args) {
try {
CharacterEncodeConverter convert = new CharacterEncodeConverter();
byte[] fullByte = convert.gbk2utf8("中文");
String fullStr = new String(fullByte, "UTF-8");
System.out.println("string from GBK to UTF-8 byte: " + fullStr);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Gbk2utf8.
*
* @param chenese the chenese
*
* @return the byte[]
*/
public byte[] gbk2utf8(String chenese) {
// Step 1: 得到GBK编码下的字符数组,一个中文字符对应这里的一个c[i]
char c[] = chenese.toCharArray();
// Step 2: UTF-8使用3个字节存放一个中文字符,所以长度必须为字符的3倍
byte[] fullByte = new byte[3 * c.length];
// Step 3: 循环将字符的GBK编码转换成UTF-8编码
for (int i = 0; i < c.length; i++) {
// Step 3-1:将字符的ASCII编码转换成2进制值
int m = (int) c[i];
String word = Integer.toBinaryString(m);
System.out.println(word);
// Step 3-2:将2进制值补足16位(2个字节的长度)
StringBuffer sb = new StringBuffer();
int len = 16 - word.length();
for (int j = 0; j < len; j++) {
sb.append("0");
}
// Step 3-3:得到该字符最终的2进制GBK编码
// 形似:1000 0010 0111 1010
sb.append(word);
// Step 3-4:最关键的步骤,根据UTF-8的汉字编码规则,首字节
// 以1110开头,次字节以10开头,第3字节以10开头。在原始的2进制
// 字符串中插入标志位。最终的长度从16--->16+3+2+2=24。
sb.insert(0, "1110");
sb.insert(8, "10");
sb.insert(16, "10");
System.out.println(sb.toString());
// Step 3-5:将新的字符串进行分段截取,截为3个字节
String s1 = sb.substring(0, 8);
String s2 = sb.substring(8, 16);
String s3 = sb.substring(16);
// Step 3-6:最后的步骤,把代表3个字节的字符串按2进制的方式
// 进行转换,变成2进制的整数,再转换成16进制值
byte b0 = Integer.valueOf(s1, 2).byteValue();
byte b1 = Integer.valueOf(s2, 2).byteValue();
byte b2 = Integer.valueOf(s3, 2).byteValue();
// Step 3-7:把转换后的3个字节按顺序存放到字节数组的对应位置
byte[] bf = new byte[3];
bf[0] = b0;
bf[1] = b1;
bf[2] = b2;
fullByte[i * 3] = bf[0];
fullByte[i * 3 + 1] = bf[1];
fullByte[i * 3 + 2] = bf[2];
// Step 3-8:返回继续解析下一个中文字符
}
return fullByte;
}
}最终的测试结果是正确的:string from GBK to UTF-8 byte: 中文。
但是这个方法并不是完美的!要知道这个规则只对中文起作用,如果传入的字符串中包含有单字节字符,如a+3中文,那么解析的结果就变成:string from GBK to UTF-8 byte: ?????????中文了。为什么呢?道理很简单,这个方法对原本在UTF-8中应该用单字节表示的数字、英文字符、符号都变成3个字节了,所以这里有9个?,代表被转换后的a、+、3字符。
所以要让这个方法更加完美,最好的方法就是加入对字符Unicode区间的判断
| UCS-2编码(16进制) | UTF-8 字节流(二进制) |
| 0000 - 007F | 0xxxxxxx |
| 0080 - 07FF | 110xxxxx 10xxxxxx |
| 0800 - FFFF | 1110xxxx 10xxxxxx 10xxxxxx |
汉字的Unicode编码范围为\u4E00-\u9FA5 \uF900-\uFA2D,如果不在这个范围内就不是汉字了。
【UTF-8转GBK】
道理和上面的相同,只是一个逆转的过程,不多说了
但是最终的建议还是:能够统一编码就统一编码吧!要知道编码的转换是相当的耗时的工作
编码与乱码(05)---GBK与UTF-8之间的转换--转载的更多相关文章
- 【Java基础专题】编码与乱码(05)---GBK与UTF-8之间的转换
原文出自:http://www.blogjava.net/pengpenglin/archive/2010/02/22/313669.html 在很多论坛.网上经常有网友问" 为什么我使用 ...
- C语言实现GBK/GB2312/五大码之间的转换(转)
源:C语言实现GBK/GB2312/五大码之间的转换 //----------------------------------------------------------------------- ...
- 聊聊计算机中的编码(Unicode,GBK,ASCII,utf8,utf16,ISO8859-1等)以及乱码问题的解决办法
作为一个程序员,一个中国的程序员,想来“乱码”问题基本上都遇到过,也为之头疼过.出现乱码问题的根本原因是编码与解码使用了不同而且不兼容的“标准”,在国内一般出现在中文的编解码过程中. 我们平时常见的编 ...
- 【UWP】解析GB2312、GBK编码网页乱码问题
在WebHttpRequest请求网页后,获取到的中文是乱码,类似这样: <title>˹ŵ��Ϸ���������� - ��̳������ - ˹ŵ��Ϸ����</title ...
- 字符编码之间的转换 utf-8 , gbk等,(解决中文字符串乱码)
目录 1.背景. 2.编码的理解 3.编码之间的相互转化 4. str类型说明 5. 可以使用的编码类型 6.参考文章 1.背景 Python中与其他程序进行交互时,如果存在字符串交互,特别是字符串中 ...
- 转载:谈谈Unicode编码,简要解释UCS、UTF、BMP、BOM等名词
转载: 谈谈Unicode编码,简要解释UCS.UTF.BMP.BOM等名词 这是一篇程序员写给程序员的趣味读物.所谓趣味是指可以比较轻松地了解一些原来不清楚的概念,增进知识,类似于打RPG游戏的升级 ...
- 各种编码中汉字所占字节数;中文字符集编码Unicode ,gb2312 , cp936 ,GBK,GB18030
vim settings set fileencodings=utf-8,ucs-bom,gb18030,gbk,gb2312,cp936,latin1set termencoding=utf-8se ...
- 【JAVA编码专题】UNICODE,GBK,UTF-8区别
简单来说,unicode,gbk和大五码就是编码的值,而utf-8,uft-16之类就是这个值的表现形式.而前面那三种编码是一兼容的,同一个汉字,那三个码值是完全不一样的.如"汉"的uncode值与g ...
- 中文字符集编码Unicode ,gb2312 , cp936 ,GBK,GB18030
中文字符集编码Unicode ,gb2312 , cp936 ,GBK,GB18030 cp936是微软自己发布的用在文件系统中的编码方式.而bg2312是中国国家标准.我明白mount -t vfa ...
随机推荐
- spring-cloud-netflix集成的服务
1.Ribbon Ribbon is a client side IPC library that is battle-tested in cloud. It provides the followi ...
- 用三层交换建立企业VLAN
650) this.width=650;" onclick='window.open("http://blog.51cto.com/viewpic.php?refimg=" ...
- C# Cache的类方法
public class DataCache { /// <summary> /// 获取当前应用程序指定CacheKey的Cache值 / ...
- Redux 总结
1.redux: function reducer(state=-,action) { switch(action.type) { case '': return ... case '': retur ...
- 学习TF:《TensorFlow机器学习实战指南》中文PDF+英文PDF+代码
从实战角度系统讲解TensorFlow基本概念及各种应用实践.真实的应用场景和数据,丰富的代码实例,详尽的操作步骤,带你由浅入深系统掌握TensorFlow机器学习算法及其实现. <Tensor ...
- Spring 热点面试题:
1.谈谈你对Springaop的理解? spring用代理类包裹切面,把他们织入到Spring管理的bean中.也就是说代理类伪装成目标类,它会截取对目标类中方法的调用,让调用者对目标类的调用都先变成 ...
- RQNOJ PID496/[IOI1999]花店橱窗布置
PID496 / [IOI1999]花店橱窗布置 ☆ 题目描述 某花店现有F束花,每一束花的品种都不一样,同时至少有同样数量的花瓶,被按顺序摆成一行,花瓶的位置是固定的,从左到右按1到V顺序 编号 ...
- Gonet2 游戏server框架解析之Agent(3)
客户端消息在Agent中的预处理流程. Agent定义好的三种请求: //api.go var RCode = map[int16]string{ 0: "heart_beat_req&qu ...
- 使用Microsoft excel 2007 进行数据分析---环境配置
使用Microsoft excel 2007 进行数据分析---环境配置 使用前须要安装SQL server 2008 data mining Add-ins for Microsoft excel ...
- Intersection between a 2d line and a conic in OpenCASCADE
Intersection between a 2d line and a conic in OpenCASCADE eryar@163.com Abstract. OpenCASCADE provid ...