【问题标题】:What is character encoding and why should I bother with it什么是字符编码,我为什么要打扰它
【发布时间】:2012-05-23 14:20:28
【问题描述】:

我对字符编码的概念很困惑

什么是Unicode、GBK等?编程语言如何使用它们?

我需要费心去了解它们吗?有没有更简单或更快的编程方式,而不必为它们烦恼?

【问题讨论】:

标签: encoding character-encoding


【解决方案1】:

(请注意,我在松散/通俗地使用其中一些术语以进行更简单的解释,但仍能抓住关键点。)

一个字节只能有 256 个不同的值,即 8 位。

由于字符集中有超过 256 个字符的字符集,因此一般不能简单地说每个字符都是一个字节。

因此,必须有描述如何将字符集中的每个字符转换为字节序列的映射。有些字符可能映射到单个字节,但其他字符必须映射到多个字节。

这些映射是编码,因为它们告诉您如何将字符编码为字节序列。

对于 Unicode,在非常高的层次上,Unicode 试图为每个字符分配一个唯一的数字。显然,这个数字必须比一个字节宽,因为有超过 256 个字符 :) Java 使用 Unicode 版本,其中每个字符都分配一个 16 位值(这就是为什么 Java 字符是 16 位宽并且具有整数值从 0 到 65535)。当你得到一个 Java 字符的字节表示时,你必须告诉 JVM 你想要使用的编码,这样它就会知道如何为字符选择字节序列。

【讨论】:

    【解决方案2】:

    ASCII 是基础

    最初 1 个字符始终存储为 1 个字节。一个字节(8 位)有可能区分 256 个可能的值。但实际上只使用了前 7 位。所以只定义了 128 个字符。该集合称为ASCII 字符集

    • 0x00 - 0x1F 包含转向代码(例如 CR、LF、STX、ETX、EOT、BEL,...)
    • 0x20 - 0x40 包含数字和标点符号
    • 0x41 - 0x7F 主要包含字母字符
    • 0x80 - 0xFF 第 8 位 = 未定义。

    法语、德语和许多其他语言需要额外的字符。 (例如à, é, ç, ô, ...)在 ASCII 字符集中不可用。所以他们用第 8 位来定义他们的字符。这就是所谓的“扩展 ASCII”。

    问题是额外的 1 位容量不足以覆盖世界上所有的语言。所以每个地区都有自己的 ASCII 变体。有许多扩展的 ASCII 编码(latin-1 是一种非常流行的编码)。

    热门问题:“ASCII 是字符集还是编码”ASCII 是一个字符集。然而,在编程中charsetencoding 被广泛用作同义词。如果我想引用只包含 ASCII 字符的编码(第 8 位始终为 0):那就是 US-ASCII

    Unicode 更进一步

    Unicode 是字符集的一个很好的例子——不是编码。它使用与 ASCII 标准相同的字符,但它使用附加字符扩展列表,从而为每个字符提供格式为 u+xxxx 的代码点。它的野心是包含全世界使用的所有字符(和流行图标)。

    UTF-8、UTF-16 和 UTF-32 是应用 Unicode 字符表的编码。但它们各自的编码方式略有不同。 UTF-8 在编码 ASCII 字符时仅使用 1 个字节,提供与任何其他 ASCII 编码相同的输出。但是对于其他字符,它会使用第一位来表示后面有第二个字节。

    GBK是一种编码,就像UTF-8一样使用多个字节。原理大同小异。第一个字节遵循 ASCII 标准,因此只使用了 7 位。但就像 UTF-8 一样,第 8 位可用于指示存在第 2 个字节,然后用它来编码 22,000 个汉字中的一个。主要区别在于这个不遵循Unicode字符集,相反它使用了一些中文字符集。

    解码数据

    当您对数据进行编码时,您会使用一种编码,但是当您对数据进行解码时,您需要知道使用了哪种编码,并使用相同的编码对其进行解码。

    不幸的是,编码并不总是被声明或指定。如果所有文件都包含一个前缀来指示它们的数据存储在什么编码中,那将是理想的。但在许多情况下,应用程序仍然只有假设或猜测他们应该使用什么编码。 (例如,它们使用操作系统的标准编码)。

    对此仍然缺乏认识,因为许多开发人员甚至不知道编码是什么。

    哑剧类型

    Mime 类型有时会与编码混淆。它们是接收器识别正在到达的数据类型的有用方法。这是一个示例,说明 HTTP 协议如何使用 mime 类型声明来定义其内容类型。

    Content-Type: text/html; charset=utf-8
    

    这是另一个造成混乱的重要原因。 mime 类型描述了消息包含的什么样的数据(例如text/xmlimage/png、...)。在某些情况下,它还会另外描述如何数据编码(即charset=utf-8)。 2点混淆:

    1. 并非所有 mime 类型都声明编码。在某些情况下,它只是可选的,有时完全没有意义。
    2. 语法charset=utf-8 增加了语义混淆,因为如前所述,UTF-8 是一种编码而不是字符集。但如前所述,有些人只是互换使用这两个词。

    例如,在text/xml 的情况下,声明编码是没有意义的(charset 参数将被忽略)。相反,XML 解析器通常会读取文件的第一行,寻找 <?xml encoding=... 标记。如果它在那里,那么他们将使用该编码重新打开文件。

    同样的问题存在when sending e-mails。电子邮件可以包含 html 消息或仅包含纯文本。在这种情况下,mime 类型也用于定义内容的类型。

    但总而言之,mime 类型并不总是足以解决问题。

    编程语言中的数据类型

    在 Java(和许多其他编程语言)的情况下,除了编码的危险之外,还将字节和整数转换为字符也很复杂,因为它们的内容存储在不同的范围内。

    • 一个字节被存储为一个有符号字节(范围:-128127)。
    • java 中的char 类型存储在 2 个无符号字节中(范围:0 - 65535
    • 流返回-1255 范围内的整数。

    如果您知道您的数据仅包含 ASCII 值。然后通过适当的技能,您可以将数据从字节解析为字符,或者立即将它们包装在字符串中。

    // the -1 indicates that there is no data
    int input = stream.read();
    if (input == -1) throw new EOFException();
    
    // bytes must be made positive first.
    byte myByte = (byte) input;
    int unsignedInteger = myByte & 0xFF;
    char ascii = (char)(unsignedInteger);
    

    快捷方式

    java中的快捷方式是使用readers和writers,并在实例化它们时指定编码。

    // wrap your stream in a reader. 
    // specify the encoding
    // The reader will decode the data for you
    Reader reader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
    

    正如前面对 XML 文件的解释,这并不重要,因为任何合适的 DOM 或 JAXB 编组器都会检查编码属性。

    【讨论】:

    • 只是一个小提示:由于几乎所有编码都以相同的方式对 128 个基本 ASCII 字符进行编码,只要在此基本集中定义了所有使用的字符,您实际上可以使用编码/解码您的消息几乎任何随机编码。 (例如 UTF-8、US-ASCII、latin-1、GBK、...)。
    • BOM(字节顺序标记)也很有趣,它用于使用多个字节的编码(例如 UTF-16)。它指示哪个字节是第一个(最重要的)。这个标记字节放在消息的前面。使用像样的Readers 的另一个好理由。
    • Unicode的字符表定义的一种编码,然而在i中却是双重编码的。 e. UTF-8。因此,Unicode 没有编码是完全错误的。
    • 是的,它是一个映射,用简单的英语来说是一个字符及其代码点的列表。 (即有间隙的编号列表)无论如何,称它为“列表”,称其为“地图”,但为避免混淆,不要称其为“编码”,这是我的观点。因为 Unicode 和 UTF-8 不可互换。它们是两种不同的东西。在我的词汇中:将字符映射到代码点不是一种编码,它只是一个字符集。 - 讨论结束(我真的觉得关于语义的讨论非常浪费时间)。
    • No “coded 字符集是一组字符,每个字符都分配了一个唯一编号。”这与我在维基百科中使用的定义相同。 ;-)
    【解决方案3】:

    字符编码是您用来解决为使用与您不同语言的人编写软件的问题的方法。

    您不知道字符是什么以及它们是如何排序的。因此,您不知道这种新语言中的字符串在二进制中会是什么样子,坦率地说,您不在乎。

    您所拥有的是一种将字符串从您说的语言翻译成他们说的语言的方法(比如翻译)。您现在需要一个能够以二进制形式表示两种语言而不会发生冲突的系统。编码就是那个系统。

    它允许您编写无论语言以二进制表示的方式如何都可以工作的软件。

    【讨论】:

      【解决方案4】:

      大多数计算机程序必须使用自然语言(人类使用的语言)的某些文本与人交流。但是计算机没有表示文本的基本方法:基本的计算机表示是由字节和字组成的位序列,硬件支持将位序列解释为固定宽度的 base-2(二进制)整数和浮点实数。因此,计算机程序必须具有将文本表示为位序列的方案。这基本上就是字符编码。字符编码没有本质上明显或正确的方案,因此存在许多可能的字符编码。

      然而,实用的字符编码有一些共同的特点。

      1. 编码文本被分成一系列字符(字形)。

      2. 每个已知的可能字符都有一个编码。文本的编码由文本字符的编码顺序组成。

      3. 每个可能的(允许的)字符都分配有一个唯一的无符号(非负)整数(这有时称为代码点)。因此,文本被编码为无符号整数序列。不同的字符编码在它们允许的字符以及它们如何分配这些唯一整数方面有所不同。大多数字符编码不允许使用已经存在并且已经存在的许多人类书写系统(脚本)使用的所有字符。因此,字符编码在它们可以表示的文本方面完全不同。即使是可以表示相同文本的字符编码也可以用不同的方式表示它,因为它们的代码点分配不同。

      4. 编码字符的无符号整数被编码为位序列。字符编码的不同之处在于它们用于此编码的位数。当这些位被分组为字节时(如流行编码的情况),字符编码的字节序可能不同。字符编码的不同之处在于它们是固定宽度(每个编码字符的位数相同)还是可变宽度(对某些字符使用更多位)。

      因此,如果计算机程序接收到旨在表示某些文本的字节序列,则计算机程序必须知道用于该文本的字符编码,如果它要执行任何类型的操作该文本(除了将其视为不透明值并原封不动地转发)。唯一的可能性是文本伴随着额外的数据,表明使用的编码程序需要(假设)文本具有特定的编码。

      类似地,如果一个计算机程序必须向另一个程序或显示设备发送(输出)文本,它必须要么告诉目的地使用的字符编码要么程序必须使用目的地的编码预计。

      在实践中,几乎所有字符编码问题都是在目的地期望使用一种字符编码发送的文本而实际使用不同的字符编码发送时引起的。这通常是由于计算机程序员没有记住存在许多可能的字符编码,并且他们的程序不能将编码文本视为不透明值,而是必须从输入并转换为输出的外部表示。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-01-15
        • 2013-10-18
        • 2018-07-11
        相关资源
        最近更新 更多