端序记号

端序记号，或称字节顺序记号（byte-order mark，BOM）是位于码点`U+FEFF`的统一码字符的名称。当以UTF-16或UTF-32来将UCS/统一码字符所组成的字串编码时，这个字符被用来标示其端序。它也被用来当做标示文件是以UTF-8、UTF-16或UTF-32编码的记号。

端序记号通常有几种涵义：

- 在16位元和32位元的情况下，文字流的端序。
- 表示文字流非常有可能是统一码编码。
- 使用的是哪一种统一码字元编码。

端序记号的使用是选择性的。它的存在会干扰那些不希望档案开头出现非ASCII字元、但可以用其他方式处理文字流的软件对于UTF-8的使用。

统一码可以以8位元、16位元或32位元整数为单位进行编码。对于16位元和32位元的表示方法，从任意来源接收文本的电脑需要知道整数是以何种端序编码的。端序记号的编码方式与文件档案的其他部分相同，如果它的字节被调换，就会变成一个非字元的统一码码位。因此，访问文本的过程中，可以透过检查这头几个字节来确定端序，而不需要文字流本身以外的一些约定或元资料。一般来说，如果有必要，接收资料的电脑会将字节换成自己的端序，不再需要端序记号进行处理。

每个统一码编码（包括统一码标准以外的编码，如UTF-7，见下表）的BOM字节序列都不一样，而且这些序列都不可能出现在以其他编码储存的文字流的开头。因此，在文字流的开头放置一个编码的BOM，可以表明文本是统一码，并识别所使用的编码方案。这种对BOM字元的使用被称为“统一码签名”。

## 使用

字符U+FEFF如果出现在字节流的开头，则用来标识该字节流的字节序，是高位在前还是低位在前。如果它出现在字节流的中间，则表达零宽度非换行空格的意义，用户看起来就是一个空格。从Unicode3.2开始，`U+FEFF`只能出现在字节流的开头，只能用于标识端序，就如它的名称——端序记号——所表示的一样；除此以外的用法已被舍弃。取而代之的是，使用`U+2060`来表达零宽度无断空白。

### UTF-8

虽然在统一码标准中，允许UTF-8也中使用端序记号，但实际上并不一定需要。UTF-8编码过的端序记号则被用来标示它是UTF-8的文件。它只用来标示一个UTF-8的档案，而不用来说明端序。但同时，该标准也不建议在有端序记号的情况下将其删除，以便在不同的编码之间转换时不会丢失资讯，并让依赖端序记号的程式能顺利运作。IETF建议，如果一个协议(a)总是使用UTF-8，或者(b)有一些其他方法来表明正在使用的编码，那么它“应该禁止使用U+FEFF作为签名”。

许多视窗程式（包含记事本）会需要添加端序记号到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。

另一方面，微软的编译器和解释器，以及许多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`）。

- 如果十六位元单位被表示成大端序，这位端序记号字符在序列中将呈现`0xFE`，其后跟着`0xFF`（其中的`0x`用来标示十六进制）。
- 如果十六位元单位使用小端序，这个字节序列为`0xFF`，其后接着`0xFE`。

这两个序列都不是有效的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”的内容将被解释为小端序，“以处理部署的内容”。然而，如果出现了端序记号，那么该记号将被视为“比其他任何东西都更有权威性”。

将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`

## 外部链接

- 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