端序记号
端序记号,或称位元组顺序记号(英语:byte-order mark,BOM)是位于码点U+FEFF
的统一码字符的名称。当以UTF-16或UTF-32来将UCS/统一码字符所组成的字串编码时,这个字符被用来标示其端序。它也被用来当做标示文件是以UTF-8、UTF-16或UTF-32编码的记号。
端序记号通常有几种涵义[1]:
端序记号的使用是选择性的。它的存在会干扰那些不希望档案开头出现非ASCII字元、但可以用其他方式处理文字流的软体对于UTF-8的使用。
统一码可以以8位元、16位元或32位元整数为单位进行编码。对于16位元和32位元的表示方法,从任意来源接收文本的电脑需要知道整数是以何种端序编码的。端序记号的编码方式与文件档案的其他部分相同,如果它的位元组被调换,就会变成一个非字元的统一码码位。因此,访问文本的过程中,可以透过检查这头几个位元组来确定端序,而不需要文字流本身以外的一些约定或元资料。一般来说,如果有必要,接收资料的电脑会将位元组换成自己的端序,不再需要端序记号进行处理。
每个统一码编码(包括统一码标准以外的编码,如UTF-7,见下表)的BOM位元组序列都不一样,而且这些序列都不可能出现在以其他编码储存的文字流的开头。因此,在文字流的开头放置一个编码的BOM,可以表明文本是统一码,并识别所使用的编码方案。这种对BOM字元的使用被称为“统一码签名”[2]。
使用
字符U+FEFF如果出现在字节流的开头,则用来标识该字节流的字节序,是高位在前还是低位在前。如果它出现在字节流的中间,则表达零宽度非换行空格的意义,用户看起来就是一个空格。从Unicode3.2开始,U+FEFF
只能出现在字节流的开头,只能用于标识端序,就如它的名称——端序记号——所表示的一样;除此以外的用法已被舍弃。取而代之的是,使用U+2060
来表达零宽度无断空白。
UTF-8
虽然在统一码标准中,允许UTF-8也中使用端序记号[3],但实际上并不一定需要[4]。UTF-8编码过的端序记号则被用来标示它是UTF-8的文件。它只用来标示一个UTF-8的档案,而不用来说明端序[5]。但同时,该标准也不建议在有端序记号的情况下将其删除,以便在不同的编码之间转换时不会丢失资讯,并让依赖端序记号的程式能顺利运作[6][7]。IETF建议,如果一个协议(a)总是使用UTF-8,或者(b)有一些其他方法来表明正在使用的编码,那么它“应该禁止使用U+FEFF作为签名”[8]。
许多视窗程式(包含记事本)会需要添加端序记号到UTF-8档案,否则将无法正确解析编码,而出现乱码。然而,在类Unix系统(大量使用文本文件,用于档案格式,用于行程间通讯)中,这种做法则不被建议采用。因为它会妨碍到如解译器脚本开头的Shebang等的一些重要的码的正确处理。它亦会影响到无法识别它的程式语言。如gcc会报告源码档开头有无法识别的字符。而在PHP中,如果没有启用输出缓冲(output buffering),它会使得页面内容开始被送往浏览器(即:用户标头档已被送出),这使PHP脚本无法指定用户标头档(HTTP Header)。端序记号在UTF-8中被表示为序列0xEF 0xBB 0xBF
,对大部分未准备好处理UTF-8的文本编辑器及网页浏览器而言,在ISO-8859-1的环境中则会显示
。
统一码标准允许在UTF-8中使用BOM,但并不要求或推荐使用它。端序在UTF-8中没有任何意义,所以它在UTF-8中的唯一用途是在开始时发出信号,表明文本流是用UTF-8编码的,或者表明它是从包含可选BOM的文本流转换到UTF-8的。该标准也不建议在有BOM的情况下将其删除,以便在不同的编码之间往返不会丢失信息,并使依赖BOM的代码继续工作。 IETF建议,如果一个协议要么(a)总是使用UTF-8,要么(b)有一些其他方法来表明正在使用的编码,那么它 "应该禁止使用U+FEFF作为签名"。
UTF-8是一种稀疏的编码,意思是很大一部分可能的字元组合不会产生有效的UTF-8文本。任何其他编码的二进制资料和文本都可能包含UTF-8无效的字元序列,唯一的例外是当文本纯粹由ASCII范围的字元组成的时候。因为所有的现代编码都使用ASCII范围的位元组来表示ASCII字元,所以无论发出这些位元组的系统打算使用什么编码,纯ASCII的文本都可以被安全地解释为UTF-8。由于这些考虑,使用启发式的分析方法可以很有把握地检测出文件是否使用UTF-8,而不需要加入BOM。 另一方面,微软的编译器[9]和解释器,以及许多Microsoft Windows上的软体,如记事本,都将BOM视为一个必要的神奇数字,而不是使用启发式分析法。这些工具在将文本保存为UTF-8时添加了BOM,并且只有在BOM存在或是文件只包含ASCII字元时才能解释UTF-8。Windows PowerShell(截至5.1版本)在保存UTF-8的XML文件档时,会添加一个BOM。然而,PowerShell Core 6在一些cmdlets上增加了一个-Encoding开关,称为“utf8NoBOM”,这样就可以在没有BOM的情况下保存文件档。Google文件在将文件档转换为纯文本文件以供下载时也会添加BOM。
UTF-16
在UTF-16中,端序记号被放置为档案或文字串流的第一个字符,以标示在此档案或文字串流中,以所有十六位元为单位的字码的端序。如果试图用错误的端序来读取这个流,位元组将被调换,从而产生字元U+FFFE
,这个字元被Unicode定义为“非字元”,不应该出现在文本中。例如,值为U+FFFE
的码位被保证将不会被指定成一个统一码字元。这意味著0xFF
、0xFE
将只能被解释成小端序中的U+FEFF
(因为不可能是大端序中的U+FFFE
)。
这两个序列都不是有效的UTF-8,所以它们的出现表明该文件不是用UTF-8编码的。
对于网际网路号码分配局注册的字元集UTF-16BE和UTF-16LE,不应该使用端序记号标记,因为这些字元集的名称已经决定了端序。如果在这样的文本串流中的任何地方遇到U+FEFF
,将被解释为一个“零宽度无断点空间”。
如果没有端序记号,可以透过搜索ASCII字元(即与0x20-0x7E范围内的字节相邻的0位元组,还有CR和LF的0x0A和0x0D)来猜测该文本是否为UTF-16及其端序。大量的(即远远高于随机)相同的顺序是UTF-16的一个非常好的指示,而0是在偶数还是奇数位元组中表明了位元组的顺序。然而,这依然可能会导致假阳性和假阴性。
Unicode标准的一致性条款D98(第3.10节)规定:“UTF-16编码方案可以以BOM开始,也可以不以BOM开始。然而,当没有BOM时,在没有高层协议的情况下,UTF-16编码方案的端序是大端序。”是否有更高层次的协议是可以解释的。例如,在一台本地端序为小端序的电脑上的文件,可能被默认为是以UTF-16LE编码。因此,大端序的推定被广泛地忽略了。在HTML5中使用的W3C/WHATWG编码标准规定,标记为“utf-16”或“utf-16le”的内容将被解释为小端序,“以处理部署的内容”[10]。然而,如果出现了端序记号,那么该记号将被视为“比其他任何东西都更有权威性”[11]。
将UTF-16解释为基于位元组的编码的程式可能会显示出乱七八糟的字元,但是ASCII字元会被识别出来,因为UTF-16表示的低字元与ASCII代码相同,因此会显示相同的字元。0的上字元可以显示为无、空白、句号,或其他一些不变的字形。
UTF-32
虽然端序记号亦可以用于UTF-32,但这个编码很少用于传输,其规则如同UTF-16。
小端序UTF-32的BOM等同小端序UTF-16的BOM图案后面加上一个NUL字元,这是一个不寻常的例子,即BOM在两种不同的编码中是相同的形式。使用BOM来识别编码的程序员必须分辨文件是UTF-32编码还是单纯以NUL作为第一个字元。
不同编码的端序记号的表示
编码 | 表示(十六进位) | 表示(十进位) |
---|---|---|
UTF-8 | EF BB BF
|
239 187 191
|
UTF-16(大端序) | FE FF
|
254 255
|
UTF-16(小端序) | FF FE
|
255 254
|
UTF-32(大端序) | 00 00 FE FF
|
0 0 254 255
|
UTF-32(小端序) | FF FE 00 00
|
255 254 0 0
|
UTF-7 | 2B 2F 76 和以下的一个位元组:[ 38 | 39 | 2B | 2F ]
|
43 47 118 和以下的一个位元组:[ 56 | 57 | 43 | 47 ]
|
UTF-1 | F7 64 4C
|
247 100 76
|
UTF-EBCDIC | DD 73 66 73
|
221 115 102 115
|
统一码标准压缩方案 | 0E FE FF
|
14 254 255
|
统一码二进制有序压缩 | FB EE 28 及可能跟随著FF
|
251 238 40 及可能跟随著255
|
GB-18030 | 84 31 95 33
|
132 49 149 51
|
另见
参考文献
- ^ FAQ - UTF-8, UTF-16, UTF-32 & BOM. Unicode.org. [2017-01-28]. (原始内容存档于2021-05-02).
- ^ The Unicode® Standard Version 9.0 (PDF). The Unicode Consortium. [2021-06-16]. (原始内容存档 (PDF)于2021-05-07).
- ^ The Unicode Standard 5.0, Chapter 2:General Structure (PDF): 36. [2009-03-29]. (原始内容存档 (PDF)于2021-04-22).
Table 2-4. The Seven Unicode Encoding Schemes
- ^ The Unicode Standard 5.0, Chapter 2:General Structure (PDF): 36. [2008-11-30]. (原始内容存档 (PDF)于2021-04-22).
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
- ^ FAQ - UTF-8, UTF-16, UTF-32 & BOM: Can a UTF-8 data stream contain the BOM character (in UTF-8 form)? If yes, then can I still assume the remaining UTF-8 bytes are in big-endian order?. [2008-03-29]. (原始内容存档于2012-09-01).
- ^ Re: pre-HTML5 and the BOM from Asmus Freytag on 2012-07-13 (Unicode Mail List Archive). Unicode.org. [2012-07-14]. (原始内容存档于2019-06-16).
- ^ Bug ID: JDK-6378911 UTF-8 decoder handling of byte-order mark has changed. Bugs.sun.com. [2017-01-28]. (原始内容存档于2017-12-19).
- ^ Yergeau, Francois. UTF-8, a transformation format of ISO 10646. IETF. November 2003 [May 15, 2014]. RFC 3629.
- ^ Alf P. Steinbach. Unicode part 1: Windows console i/o approaches. 2011 [24 March 2012]. (原始内容存档于2012-03-22).
However, since the C++ source code was encoded as UTF-8 without BOM (as is usual in Linux), the Visual C++ compiler erroneously assumed that the source code was encoded as Windows ANSI.
- ^ UTF-16LE. Encoding Standard. WHATWG. [2021-06-17]. (原始内容存档于2015-02-04).
- ^ Decode. Encoding Standard. WHATWG. [2021-06-17]. (原始内容存档于2015-02-04).
外部链接
- Unicode FAQ: UTF-8, UTF-16, UTF-32 & BOM(页面存档备份,存于互联网档案馆)
- The Unicode Standard, chapter 2.6 Encoding Schemes(页面存档备份,存于互联网档案馆)
- The Unicode Standard, chapter 2.13 Special Characters and Noncharacters, section Byte Order Mark(BOM)(页面存档备份,存于互联网档案馆)
- The Unicode Standard, chapter 16.8 Specials, section Byte Order Mark (BOM): U+FEFF(页面存档备份,存于互联网档案馆)