UTF-8 BOM
在使用Notepad++转换编码时,转为UTF-8可以选择“UTF-8”和“UTF-8 BOM”这两种,有点好奇这俩有什么区别,于是查了会资料。
References
- https://unicode.org/faq/utf_bom.html
- https://en.wikipedia.org/wiki/Byte_order_mark
- https://stackoverflow.com/questions/701624/difference-between-big-endian-and-little-endian-byte-order Difference between Big Endian and little Endian Byte order
- https://stackoverflow.com/questions/2223882/whats-the-difference-between-utf-8-and-utf-8-without-bom difference between UTF-8 and UTF-8 without BOM
- https://www.zhihu.com/question/20167122
- https://blog.csdn.net/waitingbb123/article/details/80504093
- https://baike.baidu.com/item/Endian/10830754?fr=aladdin
- https://www.zhihu.com/question/55140986
BOM的由来要从big-endian和little-endian说起了,Big Endian
(BE)(国内译为:大端) 和 Little Endian
(LE)(国内译为:小端)是由于计算机中存储数据的机制不同而产生的两种方式。计算机处理数据的最小单位是字节(byte),Big Endian
和Little Endian
都是针对多字节的序列而言的。举个栗子,如果将0x1234abcd
写入到以0x0000
开始的内存中,则结果为:
内存 | Big Endian | Little Endian |
---|---|---|
0x0000 | 0x12 | 0xcd |
0x0001 | 0x34 | 0xab |
0x0002 | 0xab | 0x34 |
0x0003 | 0xcd | 0x12 |
x86系列的CPU都是Little Endian
的字节序。C/C++语言编写的程序里数据存储顺序是跟编译平台所在的CPU相关的,而JAVA编写的程序则唯一采用Big Endian
方式来存储数据。因此,在C程序传给JAVA程序之前有必要进行字节序的转换工作。(想起来曾将因为在MFC程序中向Java服务器发消息就是因为这个问题搞了很久,泪目……)
再看一个栗子:当使用2个字节表示一个UTF-16字符时,有2种方式表示字符序列0x1234
。
Byte Index: 0 1
---------------------
Big-Endian: 12 34
Little-Endian: 34 12
问题来了,如何知道一个文本是使用了UTF-16BE
还是UTF-16LE
?规范建议在字符串前添加字节顺序标记(Byte Order Mark(BOM)),代表字符U+FEFF
。 因此,如果UTF-16编码文本的前两个字节为FE,FF,则编码为UTF-16BE。而FF,FE,就是UTF-16LE
了。
再一个形象点的栗子:单词“Example”以不同的方式编码(UTF-16 with BOM):
Byte Index: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
------------------------------------------------------------
ASCII: 45 78 61 6d 70 6c 65
UTF-16BE: FE FF 00 45 00 78 00 61 00 6d 00 70 00 6c 00 65
UTF-16LE: FF FE 45 00 78 00 61 00 6d 00 70 00 6c 00 65 00
至此,字节顺序标记(BOM)的用处就已经说明白了。那么UTF-8和BOM的关系呢?
首先, UTF-8并不存在字节序的问题。为了搞清楚UTF8不存在字节序问题,首先需要搞清楚什么是字节序问题。
对于一个32位整数1,不同的CPU架构会有不同的存储方式:
00000000 00000000 00000000 00000001
或者
00000001 00000000 00000000 00000000
故字节序问题是说对于超过8位的整数如何排布的问题,而对于像double和char数组这类类型,其实并没有影响。double一般是遵从IEEE标准的,对于各CPU都一致,而char数组更是由C代码计算地址的,不受字节序影响。所以首先要清楚,不是所有的东西都有字节序,而且字符序是以单字节为单位的顺序问题,不是字节内部的。这样问题其实就一目了然了,UTF16和UTF32的处理单元分别是2个字节和4个字节,在C语言中的定义就决定了这两个超过8位的整数需要考虑存储和网络传输的字节序。而UTF-8编码是变长的,用8~32位(1~4字节)表示,第一个字节就是第一个字节, 第二个字节就是第二个字节,需要考虑下一位时就地址+1,不会受CPU大小端的影响。有固定的位置来表示是几个字节:
0xxxxxxx
110xxxxx 10xxxxxx
1110xxxx 10xxxxxx 10xxxxxx
11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
字节头部识别就是前面的0,110,1110,11110表示字节数,头给你标出来了就不存在字节序问题了
(博主注:这段摘录的不是太好,建议去看原文(为什么 UTF-8 不存在字节序的问题?))
UTF-8 BOM是文本流开始的字节序列(0xEF,0xBB,0xBF),它可以使读取器更可靠地猜测文件是否已以UTF-8编码。
通常,BOM用来表示编码的字节序,但是由于字节序与UTF-8不相关,因此UTF-8不需要BOM。
根据Unicode标准,在UTF-8文件中使用BOM是不推荐的。
2.6 Encoding Schemes
... Use of a BOM is neither required nor recommended for UTF-8, but may be encountered in contexts where UTF-8 data is converted from other encoding forms that use a BOM or where the BOM is used as a UTF-8 signature. See the “Byte Order Mark” subsection in Section 16.8, Specials, for more information.
UTF-8和UTF-8 BOM混用会导致程序解析失败,文件打开乱码等问题,做下BOM的处理(优先考虑无BOM的格式,有问题再尝试BOM)即可。
下面的表格总结了不同UTF标准的一些属性:
Name | UTF-8 | UTF-16 | UTF-16BE | UTF-16LE | UTF-32 | UTF-32BE | UTF-32LE |
---|---|---|---|---|---|---|---|
Smallest code point | 0000 | 0000 | 0000 | 0000 | 0000 | 0000 | 0000 |
Largest code point | 10FFFF | 10FFFF | 10FFFF | 10FFFF | 10FFFF | 10FFFF | 10FFFF |
Code unit size | 8 bits | 16 bits | 16 bits | 16 bits | 32 bits | 32 bits | 32 bits |
Byte order | N/A | big-endian | little-endian | big-endian | little-endian | ||
Fewest bytes per character | 1 | 2 | 2 | 2 | 4 | 4 | 4 |
Most bytes per character | 4 | 4 | 4 | 4 | 4 | 4 | 4 |
由于标准并未明确规定,所以关于UTF-8究竟要不要带BOM依然有许多争论,目前可能仅剩微软在坚持使用UTF-8 BOM,在此也不想引发更多无意义的争论,所以将BOM的用处说清楚就结束吧。再以一个小插曲结束全文:
据Jargon File记载,endian这个词来源于Jonathan Swift在1726年写的讽刺小说 "Gulliver's Travels"(《格利佛游记》)。该小说在描述Gulliver畅游小人国时碰到了如下的一个场景。在小人国里的小人因为非常小(身高6英寸)所以总是碰到一些意想不到的问题。有一次因为对水煮蛋该从大的一端(Big-End)剥开还是小的一端(Little-End)剥开的争论而引发了一场战争,并形成了两支截然对立的队伍:支持从大的一端剥开的人Swift就称作Big-Endians,而支持从小的一端剥开的人就称作Little-Endians......(后缀ian表明的就是支持某种观点的人)。
1980年,Danny Cohen在其著名的论文"On Holy Wars and a Plea for Peace"中为了平息一场关于在消息中字节该以什么样的顺序进行传送的争论而引用了该词。该文中,Cohen非常形象贴切地把支持从一个消息序列的最高位开始传送的那伙人叫做Big-Endians,支持从最低位开始传送的相对应地叫做Little-Endians。此后Endian这个词便随着这篇论文而被广为采用。