为什么 utf8没有字节序,utf16utf32有字节序?
Posted wangjun5159
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了为什么 utf8没有字节序,utf16utf32有字节序?相关的知识,希望对你有一定的参考价值。
字节序
先看字节序的定义,援引维基百科
Endianness is the sequential order in which bytes are arranged into larger numerical values when stored in memory or when transmitted over digital links.
简单来说,字节序就是字节之间的顺序,当传输或者存储时,如果数字超过1个字节,需要指定字节间的顺序。字节序用英语说就是byte order mark,简称bom。
由来
比如,java中的int类型,它占用4个字节,比如数值1吧,翻译为十六进制就是0x[00] [00] [00] [01],最左边是最高位(most significant bit)、最右边是最低位(least significant bit),内存地址从低到高依次排列为地址1、地址2、地址3、地址4,但计算机处理时有两种处理方法,第一种是先放最高位、再放次最高位…,这种因为最高位在前,所以叫大端(big endian),简称BE;第二种是先放最低位、再放次最低位…,依次排开,这种最低位在最前边,所以叫小端(little endian)简称LE
如下
地址1 | 地址2 | 地址3 | 地址4 | 大端/小端 |
---|---|---|---|---|
00 | 00 | 00 | 01 | 大端 |
01 | 00 | 00 | 00 | 小端 |
现在,我们就把int 1,从客户端发到服务器端,既然是网络传输肯定是传字节流,好了,客户端恰巧是大端,发送的数据就是[00]、[00]、[00]、[01],但接收端的计算机恰好是小端,接收到的顺序仍然是[00]、[00]、[00]、[01],但因为是小端,所以按照小端的规则解析后就变为0x[01]、[00]、[00]、[00],看见了吧,出错了。所以在传输时,有必要说明采用的哪种字节序,一般都是在字节数组的前边插入bom。
维基百科的图
utf-8、utf-16、utf-32、bom
- utf-8在实际应用中,一般来说没有bom,因为utf-8的编码单元是字节,没有超过1个字节,所以就不存在字节序问题。当然,如果你非要加bom也无所谓。
- utf-16是以16-bit为编码单元,因为编码单元是16-bit,超过1个字节,这个时候就有字节序问题,所以最好指定bom,有utf-16LE、utf-16BE。
- utf-32是定长编码方案,它的编码单元是32-bit,同utf-16,编码单元都超过1个字节,所以需要指定bom,utf-32BE、utf-32LE
- 简单概括一下,其实utf-8、utf-16、utf-32都可以没有bom,只要传送端跟接收端一致即可,或者即使操作系统不一致,通过程序转为一致也可以。
这个地方有点绕,因为很多人说,不对呀,汉字utf-8编码是3个字节,明明超过1个字节了,为什么说没有字节序问题,这是因为utf8的编码单元是1字节,这3个字节的顺序是确定的,拿出任何一个编码单元都没超过1字节,所以不存在字节序问题。
而utf-16,编码单元是16-bit,也就是2个字节,一个字符对应1个16-bit编码单元,这2个字节,在编码单元内部排列不受编码方案约束。
实例
windows的记事本,utf8默认是带bom的
理解了上述内容,那么会理解很多东西,比如windows的记事本保存时,为什么要区分unicode 和unicode big endian了,当然了,这里的unicode就是utf-16。
另外还有一点 windows记事本的utf-8默认是带bom的,我们可以验证一下,用windows记事本保存为utf8,然后用sublime text打开,看一下编码方案
java
public void testEncode() throws UnsupportedEncodingException
String s = "你";
//将字符编码
byte[] bytesUTF8 = s.getBytes("utf-8");
byte[] byteUTF16 = s.getBytes("utf-16");
byte[] bytesUTF32 = s.getBytes("utf-32");
System.out.println(Arrays.toString(convertByte2Int(bytesUTF8))); // [228, 189, 160]
System.out.println(Arrays.toString(convertByte2Int(byteUTF16))); // [254, 255, 79, 96]
System.out.println(Arrays.toString(convertByte2Int(bytesUTF32))); // [0, 0, 79, 96],
private int[] convertByte2Int(byte[] arr)
int[] dest = new int[arr.length];
for (int i = 0; i < arr.length; i++)
byte ele = arr[i];
dest[i] = ele & 0xFF;
return dest;
第一个输出,[228, 189, 160],因为“万”字,在unicode的基本平面,占3个字节,故长度为3。utf-8一般来说没必要有bom,所以也没有bom,只有表示字符的三个字节。
第二个输出,[254, 255, 79, 96] ,“万”字,在unicode的基本平面,所以utf-16中基本平面字符占用2个字节,[79,96];而[245,255],这俩是BOM,用十六进制表示就是[FE,FF],这就是java在utf-16中添加的bom了,FEFF,是unicode规定的BOM。
第三个输出,[0, 0, 79, 96],utf-32是定长编码,所有字符占4个字节,这里没有添加bom。
综上所述,编码方案有无BOM皆可,只要传送端跟接收端一致即可,或者即使操作系统不一致,通过程序转为一致也可以。如果理解了上述知识,可以看一下java的class文件的bom,最开始是CAFE BABE,这就是java往文件中添加的BOM了
参考
以上是关于为什么 utf8没有字节序,utf16utf32有字节序?的主要内容,如果未能解决你的问题,请参考以下文章