原文出自:http://www.blogjava.net/pengpenglin/archive/2010/02/22/313669.html

在很多论坛、网上经常有网友问“ 为什么我使用 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倍

 * c.length];

        

        // Step 3: 循环将字符的GBK编码转换成UTF-8编码

; 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();

             - word.length();

            ; j < len; j++) {

                sb.append(");

            }

            // 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(");

            sb.insert(");

            System.out.println(sb.toString());



            // Step 3-5:将新的字符串进行分段截取,截为3个字节

);

            String s2 );

            String s3 );



            // Step 3-6:最后的步骤,把代表3个字节的字符串按2进制的方式

            // 进行转换,变成2进制的整数,再转换成16进制值

).byteValue();

            ).byteValue();

            ).byteValue();

            

            // Step 3-7:把转换后的3个字节按顺序存放到字节数组的对应位置

];

            bf[] = b0;

            bf[] = b1;

            bf[] = b2;

            

            fullByte[i ];            

            fullByte[i ];            

            fullByte[i ];

            

            // 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】



道理和上面的相同,只是一个逆转的过程,不多说了



但是最终的建议还是:能够统一编码就统一编码吧!要知道编码的转换是相当的耗时的工作

【Java基础专题】编码与乱码(05)---GBK与UTF-8之间的转换的更多相关文章

  1. 编码与乱码(05)---GBK与UTF-8之间的转换--转载

    原文地址:http://www.blogjava.net/pengpenglin/archive/2010/02/22/313669.html [GBK转UTF-8] 在很多论坛.网上经常有网友问“  ...

  2. Java基础专题

    Java后端知识点汇总——Java基础专题 全套Java知识点汇总目录,见https://www.cnblogs.com/autism-dong/p/11831922.html 1.解释下什么是面向对 ...

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

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

  4. Java基础——字符编码

    一.ASII 美国(国家)信息交换标准(代)码. 计算机中只有数字,一切都是用数字表示,屏幕上显示的一个一个的字符也不例外. 一个字节可表示的数字为0-255,足以显示键盘上的所有的字符 例如. a ...

  5. java基础52 编码与解码

    1.解码与编码的含义 编码:把看得懂的字符变成看不懂的码值,这个过程就叫编码    解码:根据码值查到相对应的字符,我们把这个过程就叫解码 注意:编码与解码时,我们一般使用统一的码表,否则非常容易出现 ...

  6. Java基础知识强化之IO流笔记37:FileReader/FileWriter(转换流的子类)复制文本文件案例

    1. 转换流的简化写法: 由于我们常见的操作都是使用本地默认编码,所以,不用指定编码.而转换流的名称有点长,所以,Java就提供了其子类供我们使用:FileReader / FileWriterOut ...

  7. Java 基础入门随笔(2) JavaSE版——关键字、进制转换、类型转换

    1.Java语言-关键字 关键字:被java语言赋予了特殊含义的词,特点是所有的字母都为小写. java涉及到的关键字整理: 用于定义数据类型的关键字 class interface byte sho ...

  8. java基础面试题:如何把一段逗号分割的字符串转换成一个数组? String s = "a" +"b" + "c" + "d";生成几个对象?

    package com.swift; public class Douhao_String_Test { public static void main(String[] args) { /* * 如 ...

  9. Java中long(Long)与int(Integer)之间的转换(转)

    一.将long型转化为int型,这里的long型是基础类型: long a = 10; int b = (int)a; 二.将Long型转换为int型,这里的Long型是包装类型: Long a = ...

随机推荐

  1. Maven下载 || 配置本地仓库 || IntelliJ IDEA配置Maven教程

    本文章主要介绍1.Maven下载   2.配置本地仓库Repository   3.IDEA配置Maven 三点. 相关博客: Eclipse配置Maven https://www.cnblogs.c ...

  2. 二维码的扫描和生成--第三方开源--ZXing

    ZXing的二维码功能的提取lib下载地址:https://github.com/xuyisheng/ZXingLib 1.扫描二维码: 我们扫描就是要用到这个CaptureActivity类,直接把 ...

  3. 线程存储(Thread Specific Data)

    线程中特有的线程存储, Thread Specific Data .线程存储有什么用了?他是什么意思了? 大家都知道,在多线程程序中,所有线程共享程序中的变量.现在有一全局变量,所有线程都可以使用它, ...

  4. mysql数据库优化。(强力推荐)

    本文转自:https://m.aliyun.com/yunqi/articles/38809 一个成熟的数据库架构并不是一开始设计就具备高可用.高伸缩等特性的,它是随着用户量的增加,基础架构才逐渐完善 ...

  5. Arc066_F Contest with Drinks Hard

    传送门 题目大意 有一个长为$N$的序列$A$,你要构造一个长为$N$的$01$序列使得$01$序列全部由$1$组成的子串个数$-$两个序列的对应位置两两乘积之和最大,每次独立的询问给定$pos,x$ ...

  6. ZOJ - 3201 Tree of Tree (树形背包)

    题意:有一棵树,树上每个结点都有一个权值,求恰好包含k个结点的子树的最大权值. 设dp[i][j]为以结点i为根的树中包含j个结点的子树的最大权值,则可以把这个结点下的每棵子树中所包含的所有子树的大小 ...

  7. Equation

    You are given an equation: Ax2 + Bx + C = 0. Your task is to find the number of distinct roots of th ...

  8. PHP数组编码转换

    因为一些特殊字符的显示效果的原因不得不把习惯的utf-8工程改成了GBK,由于使用了ajax技术,又涉及到了老问题——编码转换. 一些表单验证需要返回json数据,php的json_encode函数只 ...

  9. linux 标准化

    Unix 1969 年诞生于 AT&T 贝尔实验室,并在 1973 年使用 C 语言进行了重写,从此就具有了很好的可移植性.但是当 AT&T 在 1984 年由于分拆而得以进入计算机领 ...

  10. 安装部署ELK系统监控Azure China的NSG和WAF Log

    ELK是一个开源的产品,其官网是:https://www.elastic.co/ ELK主要保护三个产品: Elasticsearch:是基于 JSON 的分布式搜索和分析引擎,专为实现水平扩展.高可 ...