XML声明中的默认编码(UTF-8)有多默认?

12
我知道XML的默认编码是UTF-8。所有的XML消费者都必须等等。因此,这不仅仅是一个关于XML是否有默认编码的问题。
我也知道XML声明<?xml version="1.0" ... ?>本身是可选的。在其中指定编码也是可选的。
所以我问自己以下两个XML声明是否表示完全相同的内容:
<?xml version="1.0"?>
<?xml version="1.0" encoding="UTF-8"?>

根据我的理解,这两个声明是等价的,但我不能确定。这两个声明等价性已经在某个地方被指定了吗?

(将这两个示例行视为XML文档的第一行,前面有任何(零)字节,并以UTF-8编码)


1
幸运的是,UTF-8本身就是默认编码。当读取一个XML文档并将其写入另一种编码时,大多数情况下这个属性也会被修补。完全没有问题,我无法想象为什么经常看到编码属性。但版本很重要;更高的版本允许标签名像<café>这样。 - Joop Eggen
我问这个问题并不是因为我在字符编码方面有问题。我只是想知道它们看起来是否相同,是否有特定规定。这样就可以测试我的软件是否符合标准。 - hakre
4个回答

15

简短回答

在特定情况下,即UTF-8编码的文档没有外部编码信息(据评论所述,这是你感兴趣的内容),两个声明之间没有区别。

不过,长答案更有趣。

规范说明

如果查看XML规范的附录F1,就可以了解确定没有外部编码信息时应遵循的处理过程。

如果文档编码为UTF变体之一,解析器应该能够在前4个字节内检测到编码,无论是从字节顺序标记还是XML声明开始。

但是,根据规范,它仍应读取编码声明。

在不需要读取编码声明以确定编码的情况下,仍然需要按照第4.3.3节的要求读取编码声明(如果存在)并检查编码名称是否与实体的实际编码匹配。

如果它们不匹配,根据第4.3.3节的规定:
“...对于在声明中命名的编码之外的编码呈现给XML处理器的实体(包括编码声明),是致命错误。” 编码为UTF-16,声明为UTF-8 让我们看看当我们创建一个以UTF-16编码但编码声明设置为UTF-8的XML文档时,在现实中会发生什么。
Opera,Firefox和Chrome都将文档解释为UTF-16,忽略编码声明。Internet Explorer(至少版本9)显示空白文档,但没有实际错误。
因此,如果您在UTF-8文档上包含了UTF-8编码声明,并且后来有人将其转换为UTF-16,它将在大多数浏览器中工作,但在IE(以及我假设的大多数Microsoft XML API)中失败。如果您没有包含编码声明,那么您将没有问题。
从技术上讲,我认为IE是最准确的。它没有显示错误可能是因为错误发生在编码层而不是XML层。它假定尽力将UTF-16字符解释为UTF-8,无法找到任何可解码的字符,并最终向XML解析器传递一个空字符序列。
以其他方式声明编码的UTF-8
你现在可能认为Firefox、Chrome和Opera完全忽略了编码声明,但并非总是如此。
如果将文档编码为UTF-8(带有字节顺序标记,因此不会被误认为是其他格式),但将编码声明设置为Latin1,则所有浏览器都将成功将内容解码为Latin1,忽略UTF-8 BOM。
对我来说,这似乎是正确的。BOM字符在Latin1中无效,这意味着它们在字符解码级别上被静默丢弃。
但是,这并不适用于UTF-8文档上的所有声明编码。如果声明的编码为UTF-16,则Opera、Firefox和Chrome将忽略已声明的编码,而Internet Explorer将返回一个空文档。
基本上,任何导致IE返回空白文档的因素都会使其他浏览器忽略已声明的编码。
其他不一致之处也值得一提。根据规范第4.3.3节的规定:
实体编码为UTF-16必须以字节顺序标记开头。
然而,如果您尝试读取一个没有BOM的UTF-16编码的XML文档,大多数浏览器仍然会将其视为有效。只有Firefox会报告它为XML解析错误。
到目前为止,我们一直在考虑没有外部编码信息时会发生什么,但正如其他人所提到的,如果文档通过HTTP接收或包含在某种MIME信封中,这些来源的编码信息应优先于文档编码。
各种XML MIME类型的大部分细节都在RFC3023中描述。然而,现实情况与规定有所不同。
首先,省略字符集参数的text/xml应使用US-ASCII字符集,但这一要求几乎总是被忽略。浏览器通常会使用XML编码声明的值,如果没有则默认为UTF-8。
其次,如果文档上有UTF-8 BOM,并且XML编码声明为UTF-8或未包含,则无论Content-Type中使用的字符集如何,文档都将被解释为UTF-8。
唯一情况下Content-Type中的编码似乎具有优先权是当没有BOM并且在Content-Type中指定了显式字符集时。
无论如何,在任何涉及Content-Type的情况下,将UTF-8 XML编码声明包含在UTF-8文档中与不包含编码声明没有任何区别。

1
规范在第4.3.3节确实说明了如果编码不匹配应该发生什么:“在没有外部传输协议(例如HTTP或MIME)提供的信息的情况下,对于以声明的编码以外的编码呈现给XML处理器的实体来说,这是一个致命错误[...]”后来又说:“如果确定XML实体(通过默认值、编码声明或更高级别的协议)处于某种编码中,但包含在该编码中不合法的字节序列,则这是一个致命错误。” - nwellnhof

8
< p >就本身而言,两者是等效的。您已经引用了规范的相关部分,表明这两个声明是等效的。

然而,XML可以有一个信封,例如HTTP Content-Type头。 W3C规定,该信封信息优先于文件中的任何其他声明。因此,例如,如果您通过http检索XML,则可能会获得以下内容:

HTTP/1.1 200 OK
Content-Type: text/xml

<root/>

在这种情况下,应该将XML作为ascii读取,因为text/* MIME类型的默认字符集是ascii。这就是为什么你应该使用application/xml MIME类型 - 这些类型默认为utf-8。 "application"前缀意味着相关的应用程序规范定义了像默认编码这样的事情。(即XML规范接管了)。对于text/* MIME类型,默认值为ascii,必须在MIME类型中包含charset参数以更改字符集。
还有另一种情况:
HTTP/1.1 200 OK
Content-Type: text/xml; charset=win-1252

<?xml version="1.0" encoding="utf-8"?>
<root/>

在这种情况下,符合标准的XML处理器应该将此文件读取为win-1252,而不是utf-8
另一种情况:
HTTP/1.1 200 OK
Content-Type: application/xml

<?xml version="1.0" encoding="win-1252"?>
<root/>

这里使用的编码是 win-1252
HTTP/1.1 200 OK
Content-Type: application/xml; charset=ascii

<?xml version="1.0" encoding="win-1252"?>
<root/>

这里的编码是ascii


1
换句话说,一旦您拥有了DOM,原始文档的编码就不重要了。encoding是声明中指定的内容(例如standaloneversion),而actualEncoding是解析器解析它的方式,但所有字符串都已从文档编码转换为本地字符串。 - Francis Avila
1
我不确定你的意思。正如我所说,有两个不同的DOM属性。一个是来自处理指令的数据,另一个是来自解析器的数据。如果XML文档中没有写入encoding="....",那么DOMDocument.encoding就没有值,但是DOMDocument.actualEncoding将具有解析器用于解析文档的编码。这就是这里发生的一切。请记住,XML规范并不以DOM为中心,因此在比较文档时,您不应该过分关注确切的DOM相等性。 - Francis Avila
1
是的,但是DOM选择区分"xml声明中的encoding参数"和"解析器使用的encoding",因为正如您所看到的,它们可能是不同的。 XML信息集(与DOM相比)不包含xml声明中的encoding参数,只包含“解析器使用的”信息。 但是,如果没有xml声明,两者都可以没有值的standaloneversion`。我不确定为什么这对您来说如此惊人! - Francis Avila
1
这不是XML解析问题(XML已经被解析了),而是你特定的DOM库的低俗行为。再次强调,xmlEncoding不是标准。 - Francis Avila
1
你是正确的,应该是xmlEncodinginputEncoding。不确定我之前看到的是encodingactualEncoding。在信息集映射中可以看到,xmlEncoding/ encoding = '...'不是XML信息集的一部分。 - Francis Avila
显示剩余13条评论

5
如果第二个声明出现在已经被识别为非UTF-8兼容编码(比如UTF-16)的文档的开头,那么拒绝该声明并不是不合理的。然而,鉴于您声明该文档是UTF-8编码的,它们的处理方式没有任何区别

无论哪种情况,外部指定的编码都优先处理;两个文档仍将被视为相同。


感谢您抽出时间回答,但我这里有两个问题:首先,您链接的部分并不是规范性的。其次 - 更重要的是 - 您写了关于输入字符串的字符编码和猜测的内容。我的问题不是关于那个的,而是关于XML编码,即声明的编码方式。以及缺少声明是否真的算作缺少声明。 - hakre
你的问题的答案是肯定的,它们是相同的。我正在写关于检测编码的更多细节,为一个非常类似的情况提供更多信息,尽管它比你所问的更一般。我认为4.3.3规范中的“如果一个包含编码声明的实体以声明中未命名的编码呈现给XML处理器,或者一个既不以字节顺序标记开头也不包含编码声明的实体使用除UTF-8之外的编码,则这是一个致命错误”证实了这一点。 - Joe

1
我阅读规范的方式是,在XML声明中,UTF-8不是默认编码。它仅是“对于不以字节顺序标记或编码声明开头的实体而言的默认编码”。如果文档是UTF-16并且带有BOM,则可能具有没有编码声明或根本没有XML声明的XML声明,仍然是有效的XML。
仅对于没有BOM的文档,您提到的两个XML声明应该是等效的。

这就是为什么你在问题的结尾会发现:“(考虑这两行示例分别是XML文档的第一行,前面有任何(零)字节,并且采用UTF-8编码)” :) - hakre
没问题,这种事情很常见。 - hakre

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接