关于这个问题困惑了很长时间,主要原因是在理解“字节序”时,将“解码”也考虑进来了,在这里将解码也一并解答。
第一,编码单元与编码单元在网络中传输的顺序是确定的。即使是多字节编码方案,在网络层传输是没有问题的。比如 a b c,分别代表三个字节,发送时顺序是abc,那么接收时,仍然是abc,这个顺序不会错乱。我们经常会想utf8是多字节编码,怎么就不会存在字节序问题,这一条就很好的解答这个问题了。
第二,字节序指的是编码单元内部的字节顺序。
1、因为utf8是变长编码,而且是单字节为编码单元,不存在谁在高位、谁在低位的问题,所以不存在顺序问题!顺便说一下解码,由于utf8的首字节记录了总字节数(比如3个),所以读取首字节后,再读取后续字节(2个),然后进行解码,得到完整的字节数,从而保证解码也是正确的。
2、utf16是变长编码,使用1个16-bit编码单元或者2个16-bit编码单元,utf32是定长编码,这里拿utf16举例,在基本平面总是以2个字节为编码单元,鉴于“第一条”编码单元与编码单元之间的顺序是正确的,问题只能在编码单元内部中字节与字节的顺序,由于硬件cpu的不同,编码单元内部字节与字节的顺序不确定。假如cpu是大端序那么高位在前,如果cpu是小端序那么低位在前,为了区分,所以有了BOM(byte order mark),然后计算机才能知道谁是高位,谁是低位,知道了高低位,从而能正确组装,然后才能解码正确。
例如,一个“奎”的Unicode编码是594E,“乙”的Unicode编码是4E59。如果我们收到UTF-16字节流“594E”,那么这是“奎”还是“乙”?如果BOM是大端序,那么代码点就应该是594E,那么就是“奎”,如果BOM是小端序,那么代码点就应该是4E59,就是“乙”了。
综上所述,因为utf8是单字节为编码单元,在网络传输时,不存在字节序列问题。在解码时,由于首字节记录了总字节数,所以能正确解码。
因为utf16是定长编码,总是以2个字节为编码单元,在网络传输时,不存在字节序列问题。在解码时,由于cpu硬件差异,存在字节序问题,所以通过BOM来标记字节顺序;
理解了上述原理也就明白了为什么会有utf8、utf8-无BOM、utf16-big endian、utf16-little endian这几种保存格式了。