什么是字符编码?我为什么要麻烦它呢?

What is character encoding and why should I bother with it

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

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

我需要费心了解他们吗?有没有一种更简单或更快的编程方法,而不必麻烦自己使用它们?


(请注意,我正在松散地/通俗地使用这些术语中的一些,以获得更简单的解释,但仍然触及要点。)

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

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

因此,必须有描述如何将字符集中的每个字符转换为字节序列的映射。有些字符可能被映射到一个字节,而另一些字符则必须映射到多个字节。

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

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


ASCII是基本的

最初1个字符总是存储为1个字节。一个字节(8位)可能有256个不同的可能值。但实际上只使用了前7位。所以只定义了128个字符。这个集合称为ASCII字符集。好的。

  • 0x000x1F包含转向代码(例如CR、LF、STX、ETX、EOT、BEL等)
  • 0x200x40包含数字和标点符号
  • 0x410x7F主要包含字母字符。
  • 0x800xFF,第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字符表的编码。但它们在编码方式上各有不同。当编码一个ASCII字符时,UTF-8将只使用1个字节,提供与任何其他ASCII编码相同的输出。但对于其他字符,它将使用第一位来指示后面将跟着第二个字节。好的。

gbk是一种编码,就像utf-8使用多个字节一样。原理基本相同。第一个字节遵循ASCII标准,因此只使用7位。但是就像UTF-8一样,第8位可以用来表示第2个字节的存在,然后用它对22000个汉字中的一个进行编码。主要的区别在于,它不遵循Unicode字符集,相反,它使用一些中文字符集。好的。译码数据

当您对数据进行编码时,您使用的是编码,但当您对数据进行解码时,您需要知道使用了什么编码,并使用相同的编码对其进行解码。好的。

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

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

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

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

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

  • 并非所有的mime类型都声明编码。在某些情况下,它只是可选的,有时完全没有意义。
  • 语法charset=utf-8导致语义混乱,因为正如前面所解释的,utf-8是一种编码,而不是字符集。但正如前面所解释的,有些人只是交替使用这两个词。
  • 例如,在text/xml的情况下,声明一个编码是没有意义的(而charset参数将被简单地忽略)。相反,XML解析器通常会读取文件的第一行,查找标记。如果存在,那么他们将使用该编码重新打开文件。好的。

    发送电子邮件时也存在同样的问题。电子邮件可以包含HTML邮件或纯文本。在这种情况下,mime类型也用于定义内容的类型。好的。

    但总的来说,mime类型并不总是足以解决这个问题。好的。编程语言中的数据类型

    在Java(以及许多其他编程语言)的情况下,除了编码的危险之外,还存在向字符铸造字节和整数的复杂性,因为它们的内容被存储在不同的范围内。好的。

    • 字节存储为有符号字节(范围:-128127)。
    • Java中的EDCOX1×9类型存储在2个无符号字节中(范围:EDCOX1,10,EDCOX1,11)
    • 流返回一个范围为-1255的整数。

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

    1
    2
    3
    4
    5
    6
    7
    8
    // 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中的快捷方式是使用阅读器和编写器,并在实例化时指定编码。好的。

    1
    2
    3
    4
    // 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封送拆收器都会检查编码属性。好的。好啊。


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

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

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

    它允许您编写工作的软件,而不管语言如何用二进制表示。