原文地址: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之间的转换--转载的更多相关文章

  1. 【Java基础专题】编码与乱码(05)---GBK与UTF-8之间的转换

    原文出自:http://www.blogjava.net/pengpenglin/archive/2010/02/22/313669.html 在很多论坛.网上经常有网友问" 为什么我使用 ...

  2. C语言实现GBK/GB2312/五大码之间的转换(转)

    源:C语言实现GBK/GB2312/五大码之间的转换 //----------------------------------------------------------------------- ...

  3. 聊聊计算机中的编码(Unicode,GBK,ASCII,utf8,utf16,ISO8859-1等)以及乱码问题的解决办法

    作为一个程序员,一个中国的程序员,想来“乱码”问题基本上都遇到过,也为之头疼过.出现乱码问题的根本原因是编码与解码使用了不同而且不兼容的“标准”,在国内一般出现在中文的编解码过程中. 我们平时常见的编 ...

  4. 【UWP】解析GB2312、GBK编码网页乱码问题

    在WebHttpRequest请求网页后,获取到的中文是乱码,类似这样: <title>˹ŵ��Ϸ���������� - ��̳������ -  ˹ŵ��Ϸ����</title ...

  5. 字符编码之间的转换 utf-8 , gbk等,(解决中文字符串乱码)

    目录 1.背景. 2.编码的理解 3.编码之间的相互转化 4. str类型说明 5. 可以使用的编码类型 6.参考文章 1.背景 Python中与其他程序进行交互时,如果存在字符串交互,特别是字符串中 ...

  6. 转载:谈谈Unicode编码,简要解释UCS、UTF、BMP、BOM等名词

    转载: 谈谈Unicode编码,简要解释UCS.UTF.BMP.BOM等名词 这是一篇程序员写给程序员的趣味读物.所谓趣味是指可以比较轻松地了解一些原来不清楚的概念,增进知识,类似于打RPG游戏的升级 ...

  7. 各种编码中汉字所占字节数;中文字符集编码Unicode ,gb2312 , cp936 ,GBK,GB18030

    vim settings set fileencodings=utf-8,ucs-bom,gb18030,gbk,gb2312,cp936,latin1set termencoding=utf-8se ...

  8. 【JAVA编码专题】UNICODE,GBK,UTF-8区别

    简单来说,unicode,gbk和大五码就是编码的值,而utf-8,uft-16之类就是这个值的表现形式.而前面那三种编码是一兼容的,同一个汉字,那三个码值是完全不一样的.如"汉"的uncode值与g ...

  9. 中文字符集编码Unicode ,gb2312 , cp936 ,GBK,GB18030

    中文字符集编码Unicode ,gb2312 , cp936 ,GBK,GB18030 cp936是微软自己发布的用在文件系统中的编码方式.而bg2312是中国国家标准.我明白mount -t vfa ...

随机推荐

  1. Windows平台下使用pthreads开发多线程应用

    pthreads简介 POSIX 1003.1-2001标准定义了编写多线程应用程序的API(应用程序编程接口),这个接口通常被称为pthreads.在常见的操作系统中,例如Unix.Linux.Ma ...

  2. 【基础篇】Android手动卸载虚拟机程序

    adb shell (进入模拟器自带的操作系统) cd data/app (切换到apk的安装目录) rm apk文件全称 例 : rm com.test.TestActivity.apk (手动删除 ...

  3. SCOPE_IDENTITY()和 SELECT @@IDENTITY 的用法

    这俩个,是在插入数据的时候使用,返回刚刚插入数据行的ID 大家慎用@@IDENTITY,而尽量采用 SCOPE_IDENTITY() 函数替换之. SCOPE_IDENTITY()  也是得到最后一条 ...

  4. main函数argc,argv操作

    使用main(int argc, char *argv[])==main(int argc, char **argv)的基本操作是linux编程的最基本的一步,在windows下也是exe脱离IDE运 ...

  5. Mark Compact GC (Part two :Two-Finger)

    目录 Two-Finger算法 前提 概要 步骤一:移动对象 步骤二:更新指针 优缺点 表格算法 概要 步骤一:移动对象群 和 构筑间隙表格 移动对象群 构筑间隙表格 步骤二:更新指针 优缺点 Two ...

  6. Command "python setup.py egg_info" failed with error code 1 in /tmp/pip-build-qvc66dfs/supervisor/

    # 安装supervisor 出错 pip3 install supervisor # 解决 sudo pip3 install supervisor

  7. WPF 支持的多线程 UI 并不是线程安全的

    原文:WPF 支持的多线程 UI 并不是线程安全的 版权声明:本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可.欢迎转载.使用.重新发布,但务必保留文章署名吕毅(包含链 ...

  8. 【Uva 1625】Color Length

    [Link]: [Description] 给你两个序列,都由大写字母组成; 每次,把两个序列中的一个的开头字母加在字符串的尾端,然后在那个序列中删掉那个开头字母; 最后得到一个字符串; 这个字符串显 ...

  9. ios开发之-计算器的改进

    #import <Foundation/Foundation.h> extern double add(double x,double y); extern double subtract ...

  10. HDU - 2254 奥运 (求等比数列和)

    Description 北京迎来了第一个奥运会,我们的欢呼声响彻中国大地,所以今年的奥运金牌 day day up! 比尔盖兹坐上鸟巢里,手里摇着小纸扇,看的不亦乐乎,被俺们健儿的顽强拼搏的精神深深的 ...