UTF-8编码规则和java编码格式总结(转载从网络)

来源:互联网 发布:炉石传输数据遇到问题 编辑:程序博客网 时间:2024/06/03 08:01

UTF-8编码规则


UTF-8是一种变长字节编码方式。对于某一个字符的UTF-8编码,如果只有一个字节则其最高二进制位为0;如果是多字节,其第一个字节从最高位开始,连续的二进制位值为1的个数决定了其编码的位数,其余各字节均以10开头。UTF-8最多可用到6个字节。 

如表: 
1字节 0xxxxxxx 
2字节 110xxxxx 10xxxxxx 
3字节 1110xxxx 10xxxxxx 10xxxxxx 
4字节 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 
5字节 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 
6字节 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 
因此UTF-8中可以用来表示字符编码的实际位数最多有31位,即上表中x所表示的位。除去那些控制位(每字节开头的10等),这些x表示的位与UNICODE编码是一一对应的,位高低顺序也相同。 
实际将UNICODE转换为UTF-8编码时应先去除高位0,然后根据所剩编码的位数决定所需最小的UTF-8编码位数。 
因此那些基本ASCII字符集中的字符(UNICODE兼容ASCII)只需要一个字节的UTF-8编码(7个二进制位)便可以表示。 

对于上面的问题,代码中给出的两个字节是 
十六进制:C0 B1 
二进制:11000000 10110001 
对比两个字节编码的表示方式: 
110xxxxx 10xxxxxx 
提取出对应的UNICODE编码: 
00000 110001 
可以看出此编码并非“标准”的UTF-8编码,因为其第一个字节的“有效编码”全为0,去除高位0后的编码仅有6位。由前面所述,此字符仅用一个字节的UTF-8编码表示就够了。 
JAVA在把字符还原为UTF-8编码时,是按照“标准”的方式处理的,因此我们得到的是仅有1个字节的编码。 

大家可以试试运行这段代码: 
public class TestUTF8 { 
public static void main(String[] args) throws Exception { 
byte[][] bytes = { 
// 00110001 
{(byte)0x31}, 
// 11000000 10110001 
{(byte)0xC0,(byte)0xB1}, 
// 11100000 10000000 10110001 
{(byte)0xE0,(byte)0x80,(byte)0xB1}, 
// 11110000 10000000 10000000 10110001 
{(byte)0xF0,(byte)0x80,(byte)0x80,(byte)0xB1}, 
// 11111000 10000000 10000000 10000000 10110001 
{(byte)0xF8,(byte)0x80,(byte)0x80,(byte)0x80,(byte)0xB1}, 
// 11111100 10000000 10000000 10000000 10000000 10110001 
{(byte)0xFC,(byte)0x80,(byte)0x80,(byte)0x80,(byte)0x80,(byte)0xB1}, 
}; 
for (int i = 0; i < 6; i++) { 
String str = new String(bytes[i], "UTF-8"); 
System.out.println("原数组长度:" + bytes[i].length + 
"/t转换为字符串:" + str + 
"/t转回后数组长度:" + str.getBytes("UTF-8").length); 



运行结果为: 
原数组长度:1 转换为字符串:1 转回后数组长度:1 
原数组长度:2 转换为字符串:1 转回后数组长度:1 
原数组长度:3 转换为字符串:1 转回后数组长度:1 
原数组长度:4 转换为字符串:1 转回后数组长度:1 
原数组长度:5 转换为字符串:1 转回后数组长度:1 

原数组长度:6 转换为字符串:1 转回后数组长度:1 



java编码格式总结


乱码一直是一个很让人头疼的问题,这次往oracle的Blob里面写东西的时候,又出现了乱码。不过这次成功解决了,看了几篇讲java编码格式的文章恍然大悟,原理其实很简单,大家可以到这几个链接看看,他们写的很清楚:

 

 

http://lavasoft.blog.51cto.com/62575/273608

 

http://developer.51cto.com/art/200906/132635.htm

 

http://topic.csdn.net/u/20080822/11/69048375-6928-4a30-a58a-3ded53dc53f2.html

 

http://blog.csdn.net/fangao2620/archive/2008/02/25/2120221.aspx

 

http://jiangzhengjun.iteye.com/blog/512072

 

http://ayagen.iteye.com/blog/587653

 

其实,说白了就是,java中的String永远都是unicode编码的,以它作为中间结果转化成各种不同的编码格式,比如:

 

        String str = "中文";
        byte[] utf8b = str.getBytes("UTF-8");
        byte[] gbkb = str.getBytes("GBK");

        // 没有乱码
        System.out.println(new String(utf8b, "UTF-8"));

        // 没有乱码
        System.out.println(new String(gbkb, "GBK"));

        // 有乱码

        System.out.println(new String(gbkb, "UTF-8"));

        // 有乱码
        System.out.println(new String(utf8b, "GBK"));

 

byte数组utf8b和gbkb是不一样的,是转化成各自编码格式后的二进制数组,而new String(utf8b, "UTF-8")与new String(gbkb, "GBK")是一样的都是以Unicode编码保存。

 

我们可以来分析一下:

1. 当执行以下时:

Java代码  收藏代码
  1. byte[] utf8b = str.getBytes("UTF-8");  
 

jvm实际上是做了这样的转化 UNICODE => UTF-8,就是将Jvm内存中的unicode编码二进制码转化成UTF-8格式的二进制码然后赋值给byte[] utf8b 。这个转化的过程我们不用管,jvm会根据一个编码格式对照表来转化。

 

2. 当执行后面代码:

Java代码  收藏代码
  1. new String(utf8b, "UTF-8")  
 

实际上第二个参数"UTF-8"告诉jvm:“当前utf8b的编码格式是"UTF-8",你就以这个格式转化成unicode吧!”。也就是将utf8b转化成unicode再存入Jvm的内存,utf8=>unicode。(这个参数应该是为了告诉jvm使用“UTF-8”的编码格式对照表来转化)

 

很多时候出现乱码实际上就是第二个过程出现了问题,比如有个页面是以“UTF-8”方式编码的,当你提交了一个表单后,浏览器将表单的内容以“UTF-8”格式编码后以HTTP POST请求的方式发送到后台。当HTTP Post请求到达servlet后,提交的表单数据都是“UTF-8”编码的二进制数据。当你调用request.getParameter("someParam")的时候,servlet实际上将inputstream中“someParam”的数据取出,然后将其转化成字符转。为了便于理解,我们假设这段数据是一个byte[] pb,当你调用了request.getParameter("someParam")时相当于调用了new String(pb),由于你没有指定使用何种方式编码,因此,jvm使用系统默认的编码格式编码,这时如果你的系统默认编码格式是“UTF-8”那就相当与调用了

Java代码  收藏代码
  1. new String(utf8b, "UTF-8")  
 

不会出现乱码。但是如果你很倒霉,你的默认编码格式是“GBK”那么就好比调用了

Java代码  收藏代码
  1. new String(utf8b, "GBK")  
 

乱码就不可避免了。因为,jvm会试图将原来是"UTF-8"格式的数据使用"GBK"的对照表来转化成unicode,结果当然是牛头不对马嘴了。