Java中一个字符用unicode编码为什么不是两字节

按照之前对Unicode及utf-8的描述,一个字符以Unicode编码应该是2个字节,英文字符用utf-8占一个字节,中文可能是3-4个字节。那么我们来看看这段代码的运行结果。

import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
/**
 * @author qiang.xie
 * @date 2017/9/7
 */

public class Test {
    public static void main(String[] arg) throws Exception{
        String s1="a好";
        printBytesLen(s1,"gbk");
        printBytesLen(s1,"utf-8");
        printBytesLen(s1,"utf-16");
        printBytesLen(s1,"unicode");
        printBytesLen("h","utf-8");
        printBytesLen("h","unicode");
        printBytesLen("h","utf-16");
    }
        
    public static void printBytesLen(String string, String charset)
     throws UnsupportedEncodingException {
           byte[] bytes=string.getBytes(charset);
           System.out.println("["+string+"]使用["+charset+"]占用的字节长度:"+bytes.length+";十六进制:"+bytesToHex(bytes));
    }  
             
    //转16进制
    public static String bytesToHex(byte[] bytes){
         return new BigInteger(1,bytes).toString(16);
    }
}

运行结果:

[a好]使用[utf-8]占用的字节长度:4;十六进制:61e5a5bd
[a好]使用[utf-16]占用的字节长度:6;十六进制:feff0061597d
[a好]使用[unicode]占用的字节长度:6;十六进制:feff0061597d
[h]使用[utf-8]占用的字节长度:1;十六进制:68
[h]使用[unicode]占用的字节长度:4;十六进制:feff0068
[h]使用[utf-16]占用的字节长度:4;十六进制:feff0068

我猜你们又要凌乱了。

为什么用unicode或utf-16总数要多出两个字节?且听我一一道来。

二进制的高低位

一个16位的二级制数符占两个字节的存储空间,即高位字节和低位字节。如果你是在纸上书写一个16位的数,你总是会把高位字节写在前面,而把低位字节写在后面。然而,当这个数被存储到计算机中时,并没有固定的存储顺序。

如果我们用M和L分别表示高位字节和低位字节,那么可以有两种方式把这两个字节存储到计算机存中,即M在前L在后或者L在前M在后。

把M存储在前的顺序被称为“正向(forward)”或“高位优先(big—endian)”顺序;把L存储在前的顺序被称为“逆向(reverse)”或“低位优先(little—endian)”顺序。

big—endian这个术语的含义是数的“高位(big end)”存储在前。

大多数计算机按正向顺序存储一个数,Intel CPU按逆向顺序存储一个数,因此,如果试图将基于Intel CPU的计算机连到其它类型的计算机上,就可能会引起混乱。另外,当字节流在网络上传输时,如果你从高位到低位的顺序发送,接受者却以低位到高位的顺序结束,也会发生混乱。

那么,怎样明确的通过一些方式来表明字节的高低位顺序呢?

BOM

BOM(Byte Order Mark),字节顺序标记。

unicode编码规范中编码中有一个叫做 "Zero Width No-Break Space" ,中文译名作“零宽无间断间隔”的字符,它的编码用十六进制表示是FEFF。而 FEFF 在 unicode 中是不存在的字符,所以不应该出现在实际内容中。unicode 规范建议我们在传输字节流前,先传输这个FEFF。这样如果接收者收到 FEFF,就表明这个字节流是Big-Endian(高位在前) 的;如果收到FFFE,就表明这个字节流是 Little- Endian(低位在前)的。因此字符 "Zero Width No-Break Space" (“零宽无间断间隔”)又被称作 BOM。

说白了就是用一个专门的unicode字符来加在实际内容的前面,来告诉使用者我这个内容里的字节顺序是高位在前还是低位在前的。

如此一来,上面程序的结果也就能解释的通了。

基于unicode(utf-16)的编码总会比实际内容多出两个字节,就是那个BOM,它也是一个unicode字符,所有也需两字节。

而utf-8编码有自己特殊的特征,它完全可以根据自己的编码方式避免这个高低位顺序的问题,所以不需要BOM。

转载:https://mp.weixin.qq.com/s?timestamp=1510898006&src=3&ver=1&signature=SZlSxEWJYVinrj2MnVMDESmG2QNKElpjfJi0dW2v7FNUTVW8fikv2qxfIN8w10WiffqgevZnxVfxNpKgrni5jG5Hk28KXKjI--7lQHE*ac6EpWFtGJkYjzfxScB3F6yyrPilkbvCUJ4KruGSbajdwkMbhTE3BY1Q9yFcbhpahr0=

原文地址:https://www.cnblogs.com/taich-flute/p/7851234.html