UTF-8和带有BOM的UTF-8有什么区别?

UTF-8和BOM的UTF-8有什么不同?哪个更好?

773808 次浏览

没有BOM的UTF-8没有BOM,这并不意味着它比带有BOM的UTF-8更好,除非文件的使用者需要知道(或者知道)文件是否是UTF-8编码的。

BOM通常用于确定编码的字节顺序,这对于大多数用例来说是不需要的。

此外,对于那些不知道或不关心它的消费者来说,BOM可能是不必要的噪音/痛苦,并可能导致用户混淆。

http://en.wikipedia.org/wiki/Byte-order_mark

字节顺序标记(BOM)是Unicode字符用于表示文本文件的字节顺序或流。它的代码点是U+FEFF。BOM的使用是可选的,如果使用,应该出现在文本的开头流。除了它作为一种特定用途之外字节顺序指示器,BOM字符还可以指示哪一个几个Unicode表示文本被编码在.

始终在文件中使用BOM将确保它始终在支持UTF-8和BOM的编辑器中正确打开。

我对没有BOM的真正问题如下。假设我们有一个包含以下内容的文件:

abc

如果没有BOM,它在大多数编辑器中以ANSI形式打开。因此,此文件的另一个用户打开它并附加一些本机字符,例如:

abg-αβγ

哎呀……现在文件仍然在ANSI中,猜猜, "αβγ" 不占用6个字节,而是3个字节。这不是UTF-8,这会在开发链的后面引起其他问题。

UTF-8 BOM是文本流(0xEF, 0xBB, 0xBF)开头的字节序列,它允许读者更可靠地猜测文件是以UTF-8编码的。

通常,BOM用于表示编码的endianness,但由于endianness与UTF-8无关,因此BOM是不必要的。

根据Unicode标准不推荐UTF-8文件的BOM

2.6编码方案

… UTF-8既不需要也不建议使用BOM,但在UTF-8数据从使用BOM的其他编码形式转换或BOM用作UTF-8签名的上下文中可能会遇到。有关更多信息,请参阅第16.8节,特殊情况中的“字节顺序标记”小节。

在BOM的维基百科页面底部引用:http://en.wikipedia.org/wiki/Byte-order_mark#cite_note-2

“对于UTF-8,既不需要也不建议使用BOM,但在UTF-8数据从使用BOM的其他编码形式转换或BOM用作UTF-8签名的情况下可能会遇到”

其他优秀的答案已经回答了:

  • UTF-8和BOM-ed UTF-8之间没有官方区别
  • BOM编辑的UTF-8字符串将以以下三个字节开头。EF BB BF
  • 从文件/流中提取字符串时,必须忽略这些字节(如果存在)。

但是,作为附加信息,UTF-8的BOM可能是一个很好的方式来“闻到”一个字符串是否以UTF-8编码……或者它可能是任何其他编码中的合法字符串……

例如,数据[EF BB BF 41 42 43]可以是:

  • 合法的ISO-8859-1字符串“i”“ABC”
  • 合法的UTF-8字符串“ABC”

因此,虽然通过查看第一个字节来识别文件内容的编码可能很酷,但您不应该依赖于此,如上例所示

编码应该是已知的,而不是占卜的。

没有BOM的UTF-8和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倾向于在某处、某处繁荣(没有双关语的意思(原文如此))。当它繁荣时(例如,浏览器、编辑器等无法识别),它会在文档开头显示为奇怪的字符(例如,超文本标记语言文件、JSON响应、rss等),并导致类似最近在Twitter上谈论奥巴马时遇到的编码问题的尴尬。

当它出现在难以调试的地方或测试被忽视时,这是非常烦人的。所以最好避免它,除非你必须使用它。

我从不同的角度看待这个问题。我认为带BOM的UTF-8更好提供了有关文件的更多信息。只有当我面临问题时,我才使用没有BOM的UTF-8。

我在我的页面上使用多种语言(甚至西里尔文)很长一段时间,当文件在没有BOM的情况下保存时,我重新打开它们以使用编辑器进行编辑(如cherouvim所述),一些字符被损坏。

请注意,Windows的经典记事本自动保存文件BOM当您尝试保存一个新创建的文件与UTF-8编码。

我个人保存服务器端使用BOM编写脚本文件(. asp、. ini、. aspx). html文件没有BOM

带有BOM的UTF-8可以更好地识别。我得出了这个结论。我正在处理一个项目,其中一个结果是CSV文件,包括Unicode字符。

如果CSV文件在没有BOM的情况下保存,Excel会认为它是ANSI并显示乱码。在前面添加“EF BB BF”后(例如,使用带有UTF-8的记事本重新保存;或使用带有BOM的UTF-8的记事本++重新保存),Excel会正常打开它。

RFC 3629建议将BOM字符前置为Unicode文本文件:“UTF-8,ISO 10646的转换格式”,2003年11月在https://www.rfc-editor.org/rfc/rfc3629(最后一个信息在:http://www.herongyang.com/Unicode/Notepad-Byte-Order-Mark-BOM-FEFF-EFBBBF.html

当您想显示以UTF-8编码的信息时,您可能不会遇到问题。例如,将超文本标记语言文档声明为UTF-8,您将在浏览器中显示文档正文中包含的所有内容。

但是,当我们在Windows或Linux上有文本、CSV和XML文件时,情况并非如此。

例如,在Windows或Linux中的文本文件,可以想象的最简单的事情之一,它不是(通常)UTF-8。

将其保存为XML并声明为UTF-8:

<?xml version="1.0" encoding="UTF-8"?>

它不会正确显示(它不会被读取),即使它被声明为UTF-8。

我有一串包含法语字母的数据,需要保存为XML以进行联合。无需从一开始就创建UTF-8文件(更改IDE中的选项和“创建新文件”)或在文件开头添加BOM

$file="\xEF\xBB\xBF".$string;

我无法将法语字母保存在XML文件中。

在UTF-8编码文件中放置BOM至少有三个问题。

  1. 不包含文本的文件不再为空,因为它们始终包含BOM。
  2. 在UTF-8的ASCII子集中保存文本的文件本身不再是ASCII,因为BOM不是ASCII,这使得一些现有工具崩溃,用户可能无法替换这些遗留工具。
  3. 不可能将多个文件连接在一起,因为现在每个文件的开头都有一个BOM。

而且,正如其他人所提到的,拥有BOM来检测某个东西是UTF-8既不充分也不必要:

  • 这是不够的,因为任意字节序列可能恰好以构成BOM的确切序列开头。
  • 这不是必要的,因为您可以像读取UTF-8一样读取字节;如果成功,根据定义,它是有效的UTF-8。

如果您在超文本标记语言文件中使用UTF-8,并且在同一页面上使用塞尔维亚西里尔语、塞尔维亚拉丁语、德语、匈牙利语或某些异国语言,则带有BOM的UTF更好。

这是我的观点(30年的计算机和IT行业)。

如上所述,带有BOM的UTF-8可能会导致非BOM感知(或兼容)软件出现问题。我曾经使用基于Mozilla的KompoZer编辑过编码为UTF-8+BOM的超文本标记语言文件,因为客户端需要所见即所得程序。

保存时布局总是会被破坏。我花了一些时间来解决这个问题。这些文件在Firefox中运行良好,但在Internet Explorer中显示出CSS怪癖再次破坏了布局。在摆弄链接的CSS文件几个小时后无济于事,我发现Internet Explorer不喜欢BOMfeed超文本标记语言文件。再也不会了。

我在Wikipedia上找到了这个:

sheang字符在扩展的ASCII编码中由相同的两个字节表示,包括UTF-8,它通常用于当前类Unix系统上的脚本和其他文本文件。然而,UTF-8文件可能以可选的字节顺序标记(BOM)开头;如果“exec”函数专门检测字节0x23 0x21,那么在sheang之前存在BOM(0xEF 0xBB 0xBF)将阻止脚本解释器被执行。出于这个原因以及更广泛的互操作性和哲学问题,一些权威人士建议不要在POSIX(类Unix)脚本中使用字节顺序标记,[15]

一个实际的区别是,如果您为Mac OS X编写外壳脚本并将其保存为纯UTF-8,您将得到响应:

#!/bin/bash: No such file or directory

作为对指定您希望使用哪个shell的sheband行的响应:

#!/bin/bash

如果您保存为UTF-8,则没有BOM(在BBEdit中说)一切都会很好。

带有BOM的UTF-8仅在文件实际包含一些非ASCII字符时才会有所帮助。如果包含了它而没有任何,那么它可能会破坏将文件解释为纯ASCII的旧应用程序。当这些应用程序遇到非ASCII字符时肯定会失败,因此在我看来,BOM只应该在文件可以并且不应该再被解释为纯ASCII时添加。

我想说清楚一点,我宁愿根本没有BOM。如果一些旧垃圾没有它就坏了,添加它,替换那个遗留应用程序是不可行的。

不要期望UTF-8的BOM。

提问:没有BOM的UTF-8和UTF-8有什么不同?哪个更好?

以下是维基百科关于字节顺序标记(BOM)的文章的一些摘录,我认为它们为这个问题提供了可靠的答案。

关于BOM和UTF-8的含义:

Unicode标准允许UTF-8中的BOM,但不需要字节顺序在UTF-8中没有意义,所以它的在UTF-8中唯一使用的是在开始时发出信号,表明文本流是以UTF-8编码。

论点不是使用BOM:

不使用BOM的主要动机是向后兼容使用不支持Unicode的软件…另一个不支持的动机使用BOM是鼓励UTF-8作为“默认”编码。

论点For使用BOM:

使用BOM的论点是,没有它,启发式分析是需要确定文件正在使用的字符编码。从历史上看,这种分析,以区分各种8位编码,是复杂、容易出错,有时很慢。许多库可用于简化任务,例如Mozilla通用字符集Unicode的检测器和国际组件。

程序员错误地认为UTF-8的检测同样困难(这不是因为绝大多数字节序列是无效的UTF-8,而这些库正在尝试的编码区分允许所有可能的字节序列)。因此不是所有支持Unicode的程序执行这样的分析,而是依赖于BOM.

特别是微软编译器和解释器,以及许多Microsoft Windows上的软件(如记事本)不会正确读取UTF-8文本,除非它只有ASCII字符或它以BOM开头,并在保存文本时将BOM添加到开头作为UTF-8。当Microsoft Word文档被删除时,Google Docs将添加BOM作为纯文本文件下载。

哪一个更好,一起没有BOM:

IETF建议如果协议(a)始终使用UTF-8,或(b)有其他方法来指示正在使用的编码,然后它“应该禁止使用U+FEFF作为签名。”

我的结论:

如果与软件应用程序的兼容性是绝对必要的,请使用BOM只有

另请注意,虽然参考的维基百科文章表明许多Microsoft应用程序依赖BOM来正确检测UTF-8,但所有 Microsoft应用程序并非如此。例如,正如@陈志立所指出的,当使用UTF-8的Windows命令提示符时,typemore等命令不期望BOM存在。如果BOM存在,它可能会像其他应用程序一样有问题。


chcp命令通过代码页65001提供对UTF-8(BOM没有)的支持。

应该注意的是,对于某些文件,即使在Windows上,您也有绝不能的BOM。例如SQL*plusVBScript文件。如果此类文件包含BOM,则在尝试执行它们时会出现错误。

这个问题已经有了百万个答案,其中许多答案都很好,但是我想试着澄清什么时候应该或不应该使用BOM。

如前所述,在确定字符串是否为UTF-8时使用UTF BOM(字节顺序标记)是受过教育的猜测。如果有适当的元数据可用(如charset="utf-8"),那么你已经知道你应该使用什么,但除此之外,你需要测试并做出一些假设。这涉及检查字符串来自的文件是否以十六进制字节码EF BB BF开头。

如果找到了与UTF-8 BOM对应的字节码,则认为它是UTF-8的概率足够高,我们可以从那里开始。但是,当被迫做出这种猜测时,在读取时进行额外的错误检查仍然是一个好主意,以防出现混乱。只有当输入绝对不应该 UTF-8基于其来源时,我们才应该假设BOM不是UTF-8(即latin-1或ANSI)。但是,如果没有BOM,您可以通过验证编码来确定它是否应该是UTF-8。

为什么不推荐BOM?

  1. 非Unicode感知或不兼容的软件可能会认为它是latin-1或ANSI,并且不会从字符串中剥离BOM,这显然会导致问题。
  2. 这不是真的需要(只是检查内容是否兼容,并且在找不到兼容的编码时始终使用UTF-8作为后备)

应该用BOM编码时?

如果您无法以任何其他方式(通过字符集标记或文件系统元)记录元数据,并且程序像BOM一样使用,您应该使用BOM进行编码。在Windows上尤其如此,任何没有BOM的东西通常都被假定为使用旧代码页。BOM告诉Office等程序,是的,此文件中的文本是Unicode;这是使用的编码。

归根结底,我真正遇到问题的唯一文件是CSV。根据程序的不同,它必须或不必须有BOM。例如,如果您在Windows上使用Excel 2007+,如果您想顺利打开它,而不必求助于导入数据,它必须使用BOM编码。

以下是BOM使用的示例,这些示例实际上会导致实际问题,但许多人不知道。

BOM破坏脚本

Shell脚本,Perl脚本,Python脚本,Ruby脚本,Node.js脚本或任何其他需要由解释器运行的可执行文件-所有这些都以舍帮线开头,看起来像其中之一:

#!/bin/sh#!/usr/bin/python#!/usr/local/bin/perl#!/usr/bin/env node

它告诉系统在调用这样的脚本时需要运行哪个解释器。如果脚本是用UTF-8编码的,人们可能会想在开始时包含一个BOM。但实际上“#!”字符不仅仅是字符。它们实际上是一个魔法数字,恰好由两个ASCII字符组成。如果你在这些字符之前放了一些东西(比如BOM),那么文件看起来就像它有一个不同的魔法数字,这可能会导致问题。

参见维基百科,文章:谢邦,部分:魔法数字

sheband字符由相同的两个字节表示扩展的ASCII编码,包括UTF-8,通常用于当前类Unix系统上的脚本和其他文本文件。但是,UTF-8文件可以以可选的字节顺序标记(BOM)开头;如果“exec”函数专门检测字节0x23和0x21,然后存在BOM(0xEF 0xBB 0xBF)之前的sheband将防止脚本解释器不会被执行。一些权威建议针对在POSIX(类Unix)脚本中使用字节顺序标记,[14]出于这个原因以及更广泛的互操作性和哲学此外,在UTF-8中不需要字节顺序标记,因为该编码没有endianness问题;它仅用于将编码标识为UTF-8。[强调添加]

BOM在JSON中是非法的

RFC 7159,第8.1节

实现不得在JSON文本的开头添加字节顺序标记。

BOM在JSON中是冗余的

它不仅在JSON中是非法,在确定字符编码时也是不需要,因为有更可靠的方法可以明确地确定任何JSON流中使用的字符编码和endianness(有关详细信息,请参阅这个答案)。

BOM破坏JSON解析器

不仅JSON中的非法不需要,实际上破坏所有软件使用rfc4627中提供的方法确定编码:

确定JSON的编码和endianness,检查NUL字节的前四个字节:

00 00 00 xx - UTF-32BE00 xx 00 xx - UTF-16BExx 00 00 00 - UTF-32LExx 00 xx 00 - UTF-16LExx xx xx xx - UTF-8

现在,如果文件以BOM开头,它将如下所示:

00 00 FE FF - UTF-32BEFE FF 00 xx - UTF-16BEFF FE 00 00 - UTF-32LEFF FE xx 00 - UTF-16LEEF BB BF xx - UTF-8

注意:

  1. UTF-32BE不以三个NULs开头,因此不会被识别
  2. UTF-32LE第一个字节后面没有三个NULs,因此不会被识别
  3. UTF-16BE在前四个字节中只有一个NUL,因此无法识别
  4. 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字符导致真正问题的一个例子。

Unicode字节顺序标记(BOM)常见问题提供了一个简洁的答案:

问:我应该如何处理BOM?

A:以下是一些需要遵循的准则:

  1. 特定协议(例如. txt文件的Microsoft约定)可能需要在某些Unicode数据流上使用BOM,例如文件。当您需要遵守此类协议时,请使用BOM。

  2. 某些协议允许在未标记文本的情况下使用可选BOM。在这些情况下,

    • 已知文本数据流为纯文本,但编码未知,BOM可用作签名。如果没有BOM,编码可以是任何东西。

    • 如果已知文本数据流是纯Unicode文本(但不是哪个字节),则BOM可以用作签名。如果有如果没有BOM,则文本应解释为大端。

  3. 一些面向字节的协议期望文件开头有ASCII字符。如果UTF-8与这些协议一起使用,请使用应避免将BOM作为编码形式签名。

  4. 如果已知数据流的精确类型(例如Unicode big-endian或Unicode little-endian),则不应使用BOM。在特别是,每当数据流被声明为UTF-16BE时,UTF-16LE、UTF-32BE或UTF-32LE不得使用BOM。

这是我使用Visual Studio、源树和Bitucket拉取请求的经验,这给我带来了一些问题:

因此,在审查拉取请求时,带有签名的BOM将在每个文件上包含一个小红点字符(这可能非常烦人)。

在此输入图片描述

如果你将鼠标悬停在它上面,它会显示一个像“ufeff”这样的字符,但事实证明Sourcetree不显示这些类型的字节标记,所以它很可能最终会出现在你的拉取请求中,这应该没问题,因为这就是Visual Studio 2017现在编码新文件的方式,所以也许比特桶应该忽略这个或以另一种方式显示它,更多信息在这里:

红点标记BitBucket差异视图

我用utf-8保存了一个自动热键文件,中文字符变成了strang。

使用utf-8 BOM,工作正常。

AutoHotkey不会自动识别UTF-8文件,除非它以字节顺序标记开头。

https://www.autohotkey.com/docs/FAQ.htm#nonascii