每个开发者都应该了解的字符编码






3.89/5 (7投票s)
如果您编写的代码涉及到文本文件,您可能需要了解这些。
引言
如果您编写的代码涉及到文本文件,您可能需要了解这些。
我们先从两个关键点开始。
- Unicode (目前) 并没有完全解决这个问题。
- 每个文本文件都有编码。不存在未编码的文件或“通用”编码。
字符编码
我们再补充一点——大多数美国人,在绝大多数情况下,都可以不用考虑这个问题。因为在绝大多数编码方案中,前127个字节映射的都是相同的字符集(更准确地说是字形),而且我们只使用 A-Z 而不使用任何其他字符、重音符号等——所以我们能正常工作。但一旦您在 HTML 或 XML 文件中做出同样的假设,而文件中包含超出前127个范围的字符——那么麻烦就开始了。
计算机行业起步时,磁盘空间和内存都非常昂贵。任何建议用2个字节而不是1个字节表示一个字符的人都会被嘲笑。事实上,我们很幸运,字节最适合8位,否则每个字符可能就只有不到256位了。当然,早期开发了许多字符集(或代码页)。但最终,几乎所有人都使用了一套标准的代码页,其中前127个字节在所有代码页中都相同,而后127个字节则因集而异。有适用于美国/西欧、中欧、俄罗斯等的代码集。
然后是亚洲地区,因为256个字符不够用,所以在128-255的范围内有一些是所谓的DBCS(双字节字符集)。对于这些高位范围内的第一个字节的每个值,第二个字节会标识256个字符中的一个。这总共增加了128 * 256 个额外字符。这是一种妥协,但它最大限度地减少了内存使用。中文、日文和韩文都有自己的 DBCS 代码页。
起初,这工作得很好。操作系统、应用程序等大多被设置为使用指定的代码页。但后来互联网出现了。一个在美国的网站,使用来自希腊的 XML 文件,向一位在俄罗斯浏览的用户显示数据,而他们都基于自己的国家输入数据——这就打破了这种模式。
快进到今天。最能说明这一点,并且让所有人都会遇到问题的两种文件格式是 HTML 和 XML。每种 HTML 和 XML 文件都可以在其头部元数据中选择性地设置字符编码。如果未设置,大多数程序会假定它是 UTF-8,但这并非标准,也并非普遍遵循。如果未指定编码,而读取文件的程序猜测错误——文件就会被错误读取。
要点 1 – 在编写文件时,切勿将指定编码视为可选。务必将其写入文件。务必。即使您发誓该文件永远不会包含超出1-127范围的字符。
现在我们来看看 UTF-8,因为作为一种标准以及其工作方式,它会给人们带来很多麻烦。UTF-8 之所以流行,有两个原因。首先,它与前127个字符的标准代码页匹配,因此大多数现有的 HTML 和 XML 文件都会与其匹配。其次,它的设计是使用尽可能少的字节,这在它设计之初非常重要,因为当时许多人仍在拨号上网。
UTF-8 借鉴了亚洲代码页的 DBCS 设计。前128个字节都是单字节字符表示。然后对于下一组最常用的字符,它在第二个128个字节中使用一个块作为双字节序列,从而为我们提供了更多字符。但还有更多。对于不太常用的字符,有一个第一个字节,后面跟着一系列第二个字节。这些第二个字节又分别引出一个第三个字节,这三个字节定义了一个字符。这可以达到6个字节的序列。使用 MBCS(多字节字符集),您可以编写相当于所有 Unicode 字符的内容。并且假设您编写的内容不是一个不常用的汉字列表,那么它使用的字节数会更少。
但这是每个人都会遇到的问题——他们有一个 HTML 或 XML 文件,它运行正常,然后他们在一个文本编辑器中打开它。然后他们添加了一个字符,在他们的文本编辑器中,使用他们所在区域的代码页,插入了一个像 ß 这样的字符,然后保存了文件。当然,这一定是正确的——他们的文本编辑器显示它是正确的。但是将其输入任何根据编码读取的程序,这个字符就变成了2字节序列的第一个字符。您要么得到一个不同的字符,要么如果第二个字节对于第一个字节来说不是合法值——就会出现错误。
要点 2 – 在创建 HTML 和 XML 时,务必使用能够正确输出的程序。如果您必须使用文本编辑器创建,那么请在浏览器中查看最终文件。
那么,当您编写的代码要读取或写入文件时该怎么办?我们不是在谈论二进制/数据文件,您可以在其中以自己的格式写出数据,而是指被视为文本的文件。Java、.NET 等都有字符编码器。这些编码器的目的是在字节序列(文件)和它们表示的字符之间进行翻译。我们以一个实际上非常困难的例子为例——您的源代码,无论是 C#、Java 等。这些在很大程度上仍然是“普通的纯文本文件”,没有编码提示。那么程序是如何处理它们的呢?许多程序会假定它们使用本地代码页。还有许多程序会假定所有字符都在0-127范围内,并对任何其他字符感到困惑。
关于这些文本文件,有一个关键点——每个程序仍在使用的编码。它可能未在代码中设置,但根据定义,总会使用一种编码。
要点 3 – 在读取和写入文本文件时,务必设置编码。不仅限于 HTML 和 XML,甚至包括源代码等文件。将其设置为使用默认代码页是可以的,但要设置编码。
要点 4 – 使用最完整的编码器。您可以将自己的 XML 作为以 UTF-8 编码的文本文件编写。但是,如果您使用 XML 编码器编写,那么它会在元数据中包含编码,您就不会弄错(它还会将字节序前导符添加到文件中)。
好的,您正在正确地读写文件,但是代码内部呢?那里怎么样?这很简单——Unicode。Java 和 .NET 运行时中创建的编码器就是为此设计的。您读取时得到 Unicode。您写入 Unicode 时得到一个编码后的文件。这就是为什么 `char` 类型是16位,并且是一个独特的核心类型,用于表示字符。这部分您可能做得很好,因为如今的语言在这方面给您的选择不多。
要点 5 – (适用于使用较老语言的开发者)——在内部始终使用 Unicode。在 C++ 中,这被称为宽字符(或类似名称)。不要为了节省几个字节而耍聪明,内存很便宜,您有更重要的事情要做。
总结
我认为这里有两个关键点需要牢记。第一,确保您在处理文本文件时考虑了编码。第二,这实际上都非常简单明了。人们很少会弄错如何使用编码,而是在他们忽略这个问题时才会陷入麻烦。
历史
- 2010年12月23日:初次发布