UTF-8和UTF-8与BOM有什么不同?哪个更好?
当前回答
我从另一个角度看这个问题。我认为UTF-8与BOM更好,因为它提供了更多关于文件的信息。我只在遇到问题时才使用没有BOM的UTF-8。
我在我的页面上使用多种语言(甚至西里尔字母)很长一段时间,当文件保存时没有BOM,我重新打开它们用编辑器编辑(cherouvim也指出),一些字符被损坏了。
请注意,当您尝试以UTF-8编码保存新创建的文件时,Windows的经典记事本会自动保存带有BOM的文件。
我个人保存带有BOM的服务器端脚本文件(.asp, .ini, .aspx)和没有BOM的.html文件。
其他回答
这个问题已经有了无数个答案,其中许多答案都很好,但我想尝试并澄清何时应该使用BOM,何时不应该使用BOM。
如前所述,任何使用UTF BOM(字节顺序标记)来确定字符串是否为UTF-8的方法都是有根据的猜测。如果有适当的元数据可用(如charset="utf-8"),那么您已经知道应该使用什么,但除此之外,您还需要进行测试并做出一些假设。这涉及到检查字符串来自的文件是否以十六进制字节码EF BB BF开头。
If a byte code corresponding to the UTF-8 BOM is found, the probability is high enough to assume it's UTF-8 and you can go from there. When forced to make this guess, however, additional error checking while reading would still be a good idea in case something comes up garbled. You should only assume a BOM is not UTF-8 (i.e. latin-1 or ANSI) if the input definitely shouldn't be UTF-8 based on its source. If there is no BOM, however, you can simply determine whether it's supposed to be UTF-8 by validating against the encoding.
为什么不推荐使用BOM ?
不支持unicode或兼容性较差的软件可能会假定它是latin-1或ANSI,并且不会从字符串中剥离BOM,这显然会导致问题。 这并不是真正需要的(只要检查内容是否兼容,并且在找不到兼容编码时总是使用UTF-8作为备用)
什么时候应该使用BOM编码?
如果您无法以任何其他方式(通过字符集标记或文件系统元)记录元数据,并且像使用BOM一样使用程序,则应该使用BOM进行编码。在Windows上尤其如此,没有BOM的任何东西通常都被认为使用了遗留代码页。BOM告诉Office等程序,是的,这个文件中的文本是Unicode;这是使用的编码。
归根结底,我唯一真正有问题的文件是CSV。根据程序的不同,它必须或必须没有BOM。例如,如果你在Windows上使用Excel 2007+,如果你想要顺利地打开它,而不必求助于导入数据,它必须用BOM编码。
没有BOM的UTF-8没有BOM,这并不意味着它比有BOM的UTF-8更好,除非文件的消费者需要知道(或者从知道中受益)文件是否是UTF-8编码的。
BOM通常用于确定编码的字节序,这对于大多数用例来说是不需要的。
此外,对于那些不了解或不关心BOM的消费者来说,BOM可能是不必要的噪音/痛苦,并可能导致用户困惑。
以下是我使用Visual Studio、Sourcetree和Bitbucket拉请求的经验,这给了我一些问题:
因此,在审查拉取请求时,带有签名的BOM将在每个文件上包含一个红点字符(这可能非常烦人)。
如果你把鼠标停在上面,它会显示一个像“ufeff”这样的字符,但事实证明Sourcetree不显示这些类型的字节标记,所以它很可能会在你的拉请求中结束,这应该是可以的,因为这是Visual Studio 2017现在编码新文件的方式,所以也许Bitbucket应该忽略这个或让它以另一种方式显示,更多信息在这里:
红点标记BitBucket差异视图
UTF-8和没有BOM的UTF-8有什么不同?
简单回答:在UTF-8中,BOM编码为文件开头的字节EF BB BF。
长一点的回答:
最初,预计Unicode将以UTF-16/UCS-2编码。BOM是为这种编码形式设计的。当您有2字节的代码单元时,有必要指出这两个字节的顺序,这样做的一个常见惯例是在数据的开头包含字符U+FEFF作为“字节顺序标记”。字符U+FFFE是永久未分配的,因此可以使用它来检测错误的字节顺序。
不管平台字节顺序如何,UTF-8都具有相同的字节顺序,因此不需要字节顺序标记。然而,它可能出现在从UTF-16转换为UTF-8的数据中(作为字节序列EF BB FF),或者作为表示数据为UTF-8的“签名”。
哪个更好?
没有。正如Martin Cote回答的那样,Unicode标准并不推荐这样做。它会导致非bom识别软件出现问题。
检测文件是否为UTF-8的更好方法是执行有效性检查。UTF-8对哪些字节序列是有效的有严格的规则,因此假阳性的概率可以忽略不计。如果一个字节序列看起来像UTF-8,那么它可能就是。
下面是一些实际导致问题的BOM使用示例,但许多人并不了解它。
BOM中断脚本
Shell脚本,Perl脚本,Python脚本,Ruby脚本,Node.js脚本或任何其他需要由解释器运行的可执行文件-都以shebang行开始,看起来像这样:
#!/bin/sh
#!/usr/bin/python
#!/usr/local/bin/perl
#!/usr/bin/env node
它告诉系统在调用这样的脚本时需要运行哪个解释器。如果脚本是用UTF-8编码的,人们可能会在开头包含一个BOM。但实际上“#!”字符不仅仅是字符。它们实际上是一个神奇的数字,恰好由两个ASCII字符组成。如果您在这些字符之前放了一些东西(如BOM),那么文件看起来就像有一个不同的魔法数字,这可能会导致问题。
参见维基百科,文章:Shebang,章节:魔法数字:
The shebang characters are represented by the same two bytes in extended ASCII encodings, including UTF-8, which is commonly used for scripts and other text files on current Unix-like systems. However, UTF-8 files may begin with the optional byte order mark (BOM); if the "exec" function specifically detects the bytes 0x23 and 0x21, then the presence of the BOM (0xEF 0xBB 0xBF) before the shebang will prevent the script interpreter from being executed. Some authorities recommend against using the byte order mark in POSIX (Unix-like) scripts,[14] for this reason and for wider interoperability and philosophical concerns. Additionally, a byte order mark is not necessary in UTF-8, as that encoding does not have endianness issues; it serves only to identify the encoding as UTF-8. [emphasis added]
BOM在JSON中是非法的
参见RFC 7159,章节8.1:
实现绝对不能在JSON文本的开头添加字节顺序标记。
BOM在JSON中是冗余的
它不仅在JSON中是非法的,而且也不需要确定字符编码,因为有更可靠的方法可以明确地确定任何JSON流中使用的字符编码和字节顺序(详细信息请参阅这个答案)。
BOM 会中断 JSON 解析器
它不仅在JSON中是非法的,而且不需要,它实际上破坏了所有使用RFC 4627中提出的方法来确定编码的软件:
确定JSON的编码和字节顺序,检查前四个字节为NUL字节:
00 00 00 xx - UTF-32BE
00 xx 00 xx - UTF-16BE
xx 00 00 00 - UTF-32LE
xx 00 xx 00 - UTF-16LE
xx xx xx xx - UTF-8
现在,如果文件以BOM开头,它将看起来像这样:
00 00 FE FF - UTF-32BE
FE FF 00 xx - UTF-16BE
FF FE 00 00 - UTF-32LE
FF FE xx 00 - UTF-16LE
EF BB BF xx - UTF-8
注意:
UTF-32BE不是以三个null开头的,所以它不会被识别 UTF-32LE第一个字节后面没有三个null,所以它不会被识别 UTF-16BE在前四个字节中只有一个NUL,所以它不会被识别 UTF-16LE在前四个字节中只有一个NUL,所以它不会被识别
根据实现的不同,所有这些都可能被错误地解释为UTF-8,然后被错误地解释或拒绝为无效的UTF-8,或者根本不被识别。
此外,如果实现像我推荐的那样测试有效的JSON,它甚至会拒绝确实编码为UTF-8的输入,因为它没有像RFC那样以< 128的ASCII字符开头。
其他数据格式
JSON格式的BOM是不需要的,是非法的,并且会破坏按照RFC正常工作的软件。当时不使用它应该是显而易见的,然而,总是有人坚持通过使用bom、注释、不同的引用规则或不同的数据类型来破坏JSON。当然,如果你需要,任何人都可以自由地使用像bom或其他东西——只是不要把它称为JSON。
对于JSON以外的其他数据格式,看看它到底是什么样子的。如果唯一的编码是UTF-*,并且第一个字符必须是小于128的ASCII字符,那么您已经拥有了确定数据编码和字节序所需的所有信息。即使将bom添加为可选特性,也只会使其更加复杂和容易出错。
BOM的其他用途
至于JSON或脚本之外的用途,我认为这里已经有了很好的答案。我想添加更详细的信息,特别是关于脚本和序列化,因为这是BOM字符导致实际问题的一个例子。
推荐文章
- Java标识符中的“连接字符”是什么?
- 使用Javascript的atob解码base64不能正确解码utf-8字符串
- 为什么字符集名称不是常量?
- 编码字符串为UTF-8
- 有没有统一码符号来表示"搜索"
- 我如何在PHP中输出一个UTF-8 CSV, Excel将正确读取?
- Python __str__与__unicode__
- 如何在Python中将字符串转换为utf-8
- Unicode和UTF-8的区别是什么?
- 我真的需要将“&”编码为“&”吗?
- 用Python写入UTF-8文件
- c++中的_tmain()和main()有什么区别?
- HTML编码问题-显示“”字符而不是“ ”
- 将Unicode文本写入文本文件?
- PHP DOMDocument loadHTML没有正确编码UTF-8