【问题标题】:Editor displays chinese characters when xml-file with utf-16 is opened打开带有 utf-16 的 xml 文件时,编辑器显示中文字符
【发布时间】:2013-09-20 18:24:12
【问题描述】:

我已经发布了一个 xml-utf16 问题 Emacs displays chinese character if I open xml file 但现在我想了解为什么会出现这种问题。也许,如果我有更深入的了解,我可以更好地应对这类问题。

具体来说,我得到了一个用 utf16 编码的 xml 文件。我用emacs(记事本,firefox)从我的windows xp PC打开文件,并显示了图(A)(firefox说:格式不正确)。显然,该文件是使用编码 utf16 导出的。 (B) 显示十六进制版本。 (C) 显示用 emacs (revert-buffer-with-coding-system) 转换为 utf-8 后的 xml 文件。我还使用 Perl 将 xml-utf16 文件转换为 utf8。结果显示在 (D) 中。

我的问题:

  1. 显然,xml 文件是使用编码 utf-16le 导出的。据我了解,utf-16 是一种比 utf-8 更简单、更古老的编码。为什么 utf-8 不理解这种编码?以及为什么编辑器显示汉字?
  2. 如果我想读取 xml 文件的内容,建议使用 emacs 进行转换。由于“@”,我得到的不是很可读(C)。我认为编码问题是一项常见的任务,像 emacs 这样的编辑器可以应付。我错了还是这个问题(插入“@”)是由于 xml 文件的错误规范?为什么字符之间的十六进制版本有一个点?
  3. 我从 Internet 下载了一个 Perl 代码,它将 utf16 转换为 utf8。如果我将原始 xml 文件转换为 utf-8,我得到了图 (D)。好在 firefox 显示新 xml 文件的树结构。这不是使用 emacs (D) 的情况。整个内容写在一行中(第一行除外)。实际上,原始文件不包含 CR 或 LF。如果我想查看考虑树结构的 utf16/utf8 xml 文件,我的工作似乎是编写 Perl 或 Python 代码,通过插入 CR/LF 或使用适当的 Perl 来考虑树结构/Python 包,不是吗?
  4. 为什么导出数据并生成正在研究的 xml 文件的导出器在被编辑器打开时不考虑 LF/CR 以获得可读的 xml 文件?这是为了避免大文件吗?
  5. 关于 utf16 (https://softwareengineering.stackexchange.com/questions/102205/should-utf-16-be-considered-harmful) 存在争议。使用 utf16 显然存在问题,这个问题是大约 4 年前提出的。为什么程序员仍然使用 utf16?我错过了什么吗? (我想建议我的数据交付者使用 utf8)。

感谢您的耐心。

【问题讨论】:

  • 您是否尝试过在编码声明中指定字节序?

标签: xml perl emacs utf-8 utf-16


【解决方案1】:

为什么 utf-8 不理解这种编码?

嗯? UTF-8 是一种编码。它不理解编码。你的editor是懂编码的,它对UTF-8、UTF-16le和UTF-16be的理解不一定相关。

为什么编辑器显示汉字?

(A) 中的问题是您的编辑器正在使用 UTF-16be 来解码使用 UTF-16le 编码的文档。

我错了还是这个问题(插入“@”)是由于 xml 文件的错误规范?

文件是正确的。该文档使用 UTF-16le。它通过使用 encoding="utf-16" 和 BOM 来指定它使用 UTF-16le。

(C) 中的问题是您的编辑器使用单字节编码来解码使用 UTF-16le 编码的文档。 ^@ 表示 NUL。

以及为什么字符之间的十六进制版本中有一个点?

没有。最右边的列显示使用 US-ASCII 解码的文件的内容,这显然不是。

如果我想查看考虑树结构的 utf16/utf8 xml 文件

...那么您将需要一个以该形式显示 XML 的 XML 查看器/编辑器,而不是文本编辑器。

为什么导出数据并生成正在研究的 xml 文件的导出器在被编辑器打开时不考虑 LF/CR 以获得可读的 xml 文件?

更简单。

关于utf16有争论

同样的问题发生在 UTF-8 上,因为人们没有正确处理字素。如果您正确处理字素,UTF-16 的“问题”就会消失。

因此,拒绝 UTF-16 是因为它是一种可变宽度编码,而且很少有人认为它对我来说毫无意义,因为 UTF-8 也是如此。

为什么程序员仍然使用 utf16?我错过了什么吗?

这是 Windows 内部使用的。

我想建议我的数据交付者使用 utf8

对于您在 emacs 中错误地使用 UTF-16be 而不是 UTF-16le 来说,这似乎是一个相当激进的解决方案。

【讨论】:

  • (不小心过早地发帖了。现在帖子的剩余部分。)
  • 非常感谢您对我的问题的简洁回答。对你上面的“呵呵”:我认为 utf8 是 utf16 的超集。以及“使用单字节编码”:我必须弄清楚这意味着什么以及如何解决。
  • UTF-8 不是 UTF-8 的超集。事实上,没有任何代码点在 UTF-8 和 UTF-16le 或 UTF-16be 中编码相同。 UTF-16le/be 是 UCB-2le/be 的超集,因为后者只能编码 U+0000..U+FFFF。
  • amon 注意到了一些我没有注意到的东西(CRLF 的问题)。我会留下我的答案,但这不是完整的画面。
【解决方案2】:

有很多事情你似乎不知道:

  • 什么是字符,什么是编码?
  • 什么是 Unicode?​​li>
  • 什么是各种 Unicode 编码,它们有何不同,它们的优点和缺点是什么,它们的历史是什么?
  • XML 规范对编码有何规定?
  • 各种操作系统如何与编码交互?
  • 如何直观地表示二进制数据?
  • XML 中的空格有什么作用?

基础知识

这只是指向“The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!)” by Joel Spolsky 的链接。

TL;DR:编码是双射偏函数,将字节序列映射到字符序列并再次映射回来。 Unicode 是一个大字符列表,每个字符都有一个数字(代码点)。各种编码用于将这些代码点映射到字节:

  • ASCII,只能表示 128 个不同的字符。
  • UTF-16,每个代码点至少使用两个字节。这可以包括空字节。这种编码是模棱两可的:从哪个方向读取字节?字节顺序标记 0xFEFF0xFFFE 对此进行排序,其中一个位于每个 UTF-16 文档之前。
  • UTF-8 对每个字符至少使用一个字节,并且具有 ASCII 是 UTF-8 子集的属性。它不能包含空字节(嗯,除了实际的 NUL)。这种编码的缺点是非常高的代码点具有大的表示。 CJK 文本在 UTF-16 中可以用比 UTF-8 更少的字节来表示。对于西方文本,情况正好相反。

二进制数据的可视化表示

某些字符(“控制字符”)没有可打印的解释。在您的 hexdump 中,不可打印的字节用 . 表示。 Emacs 和 Vim 沿用了控制代码前缀 ^ 的传统路线,这意味着它与下一个字符一起代表一个控制代码。 ^@ 表示 NUL 字符,^H 表示退格,^D 表示传输结束。您可以通过从视觉表示中的 ASCII 字符中减去 0x40 来获得控制字符的 ASCII 值。 \3770xFF 的八进制表示。

XML 和编码

XML 的默认编码是 UTF-8,因为它向后兼容 ASCII。正如这个问题所证明的,使用任何其他编码都是不必要的痛苦。无论如何,如果正确声明(您的输入尝试),可以使用 UTF-16 ,但随后会变得混乱。

您的输入有问题。

您的文件包含以下部分:

  • BOM 0xFFFE,表示第一个字节是输入中的低字节。 ASCII 字符后跟一个 NUL 字节。
  • 输入的第一行(最多 hexdump 中的字节 0x52)包括正确编码的 XML 声明。
  • 然后,坏事发生了:我们得到了序列0d00 0d0a0d00CR,回车。第二部分是0a00,换行符。它们一起形成了一个 Windows 行尾。 0d0a 将是一个 ASCII CRLF。但这是错误的,因为 UTF-16 是一种两字节编码。
  • 之后,UTF-16 继续,但现在 NUL 在每个字符之前:另一个 UTF-16 版本!但你的编辑不知道这一点,给你漂亮的汉字。

发生了什么:

  1. 有人打印出以 UTF-16le 编码的 XML 序言。最后的\n 自动翻译为\r\n。所以0d00 0a00 变成了0d00 0d0a 00

    当您不对输入进行解码而是对输出进行编码时,这可能会在 Perl 中发生。在 Windows 上,Perl 会自动进行换行转换,这可以通过binmode $fh 关闭。

  2. 文档的其余部分打印在一行中,因此没有发生进一步的翻译。因为添加一个字节改变了一切,解释发生了巨大的变化。

如果您的脚本可以修复此错误,那么它在反向时也犯了同样的错误(将\r\n 翻译成\n,然后然后对其进行解码)。

可以通过直接解码所有输入并在打印之前再次对其进行编码来避免此类错误。在内部,始终对代码点进行操作,而不是字节。在 Perl 中,可以使用 binmode 将编码添加到文件句柄中,从而透明地执行反编码和编码。

【讨论】:

  • 非常感谢您提供的大量 cmets。我需要一些时间来完成它。也许这将是我经历过的最陡峭的学习曲线。
猜你喜欢
  • 2013-01-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-06-27
  • 1970-01-01
  • 2021-03-09
  • 1970-01-01
相关资源
最近更新 更多