如果一个字节,以10开始,表示它不是首字节,需要向前查找才能得到当前字符的首字节。

可见UTF-8可以有效地保证数据的完整性,避免出现编码的错位。即使偶然出现“坏字”,也不会影响到后续的文本。

对于在BMP中的中文字来说,需要用3个字节才能表示,比使用UTF-16或直接使用双字节的GB2312编码大了0.5倍。

Unicode签名bom详细说明所谓的unicode保存的文件实际上是utf-16,只不过恰好跟unicode的码相同而已,但在概念上unicode与utf是两回事,unicode是内存编码表示方案,而utf是如何保存和传输unicode的方案。utf-16还分高位在前(LE)和高位在后(BE)两种(big

endian和little

endian称作“大尾”和“小尾”,)。官方的utf编码还有utf-32,也分LE和BE。非unicode官方的utf编码还有utf-7,主要用于邮件传输。utf-8的单字节部分是和iso-8859-1兼容的,这主要是一些旧的系统和库函数不能正确处理utf-16而被迫出来的,而且对英语字符来说,也节省保存的文件空间(以非英语字符浪费空间为代价)。在iso-8859-1的时候,utf8和iso-8859-1都是用一个字节表示的,当表示其它字符的时候,utf-8会使用两个或三个字节。

后记:UTF-8以字节为编码单元,它的字节顺序在所有系统中都是一様的,没有字节序的问题,也因此它实际上并不需要BOM。

unicode有具体对应码表,比如\t0061(十六进制,对应'a';00高位,61低位),但是实际字节数组是用be,还是le表示都不太相同,比如java使用utf-16be,而有些语言使用utf-16le(微软把这个才叫unicode),可以通过bom推断高低位再组装数据。

java的unicode内存表示默认使用是UTF-16BE。

我这里写了一个例子,把字符串”我是好人”,以unicode编码写入到文本中,用InputStreamReader以”UTF-16LE”编码读取,输出的结果为:“???絙?”,而以“UTF-16BE”编码读取,结果则是“?我是好人”,文件大小显示为10字节。我的理解是,如果程序中指定使用unicode编码,但是没有指定字节顺序,程序会用java默认的UTF-16BE进行编码,并且,会输出标志位FEFF。所以文件中多了两个字节。而读取的时候,使用unicode编码读取,程序不知道使用的是哪一种字节顺序,先读取开头的FEFF,结果表明是采用的UTF-16BE编码,接下来再继续解析,就得到正确字符。而如果指定使用UTF-16LE编码,因为已经包含了字节顺序,所以就直接从字节数组中读取形成字符串,就出现了五个字符,而且由于编码格式不一样,除了‘好‘和‘絙’的编码刚好高低位倒置,还可以形成字符,其余全都是乱码。而使用UTF-16BE编码读取,因为已经制定了字节顺序,所以,也就不去读取标志位,那么就把unicode中无法编码的FEFF解码成为了‘?’。

比如字符0xabcd,它的存储格式到底是 AB CD,还是 CD AB 呢?

实际上两者都有可能,并分别有不同的名字。如果存储为 AB CD,则称为Big Endian; 如果存储为 CD

AB,则称为Little Endian。值(0xabcd)的高位(0xab)。

Big Endian解释

最低位地址存放高位字节,可称高位优先,内存从最低地址开始按顺序存放(高数位数字先写)。最高位字节放最前面。例如“汉”字的Unicode编码是6C49。如果将6C写在前面,就是big

endian。还是将49写在前面,就是little endian

Unicode规范中推荐的标记字节顺序的方法是BOM。BOM不是“Bill Of Material”的BOM表,而是Byte

Order Mark。BOM是一个有点小聪明的想法:

在UCS编码中有一个叫做"ZERO WIDTH NO-BREAK

SPACE"的字符,它的编码是FEFF。而FFFE在UCS中是不存在的字符,所以不应该出现在实际传输中。UCS规范建议我们在传输字节流前,先传输字符"ZERO

WIDTH NO-BREAK SPACE"。

这样如果接收者收到FEFF,就表明这个字节流是Big-Endian的;如果收到FFFE,就表明这个字节流是Little-Endian的。因此字符"ZERO

WIDTH NO-BREAK SPACE"又被称作BOM。

其实big endian是指低地址存放最高有效字节(MSB),而little

endian则是低地址存放最低有效字节(LSB)。