我对字符编码的概念感到很困惑。
什么是Unicode,GBK等?编程语言如何使用它们?
我需要打扰他们吗?是否有更简单或更快速的编程方式而不必麻烦自己?
答案 0 :(得分:36)
(请注意,我正在松散地/通俗地使用这些术语中的一些用于更简单的解释,仍然是关键点。)
一个字节只能有256个不同的值,即8位。
由于字符集中有超过256个字符的字符集,因此通常不能简单地说每个字符都是一个字节。
因此,必须有映射描述如何将字符集中的每个字符转换为字节序列。某些字符可能映射到单个字节,但其他字符必须映射到多个字节。
这些映射是编码,因为它们告诉你如何将字符编码为字节序列。
对于Unicode,在非常高的层次上,Unicode是尝试为每个字符分配一个唯一的数字。显然,这个数字必须比一个字节宽,因为有超过256个字符:) Java使用Unicode版本,其中每个字符都分配了一个16位值(这就是为什么Java字符是16位宽并且有整数的原因值从0到65535)。当您获得Java字符的字节表示时,您必须告诉JVM您要使用的编码,以便它知道如何为字符选择字节序列。
答案 1 :(得分:36)
最初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
是一个字符集。但是,在编程中charset
和encoding
被广泛用作同义词。如果我想引用一个只包含ASCII字符的编码而已(第8位始终为0):那就是US-ASCII
。
Unicode是字符集的一个很好的例子 - 不是编码。它使用与ASCII标准相同的字符,但它使用其他字符扩展列表,从而为每个字符提供格式为u+xxxx
的代码点。它的目标是包含整个世界中使用的所有角色(和流行的图标)。
UTF-8,UTF-16和UTF-32是应用Unicode字符表的编码。但它们各自在如何编码它们方面略有不同。 UTF-8在编码ASCII字符时仅使用1个字节,提供与任何其他ASCII编码相同的输出。但是对于其他字符,它将使用第一个位来指示第二个字节将跟随。
GBK是一种编码,就像UTF-8使用多个字节一样。原理基本相同。第一个字节遵循ASCII标准,因此只使用7位。但就像UTF-8一样,第8位可用于指示第二个字节的存在,然后它用于编码22,000个中文字符之一。主要区别在于,不遵循Unicode字符集,相比之下它使用了一些中文字符集。
对数据进行编码时,使用编码,但在解码数据时,您需要知道使用了哪种编码,并使用相同的编码对其进行解码。
不幸的是,编码并不总是被声明或指定。如果所有文件都包含一个前缀来指示其数据存储的编码,那将是理想的。但在许多情况下应用仍然是只需要假设或猜测他们应该使用什么编码。 (例如,他们使用操作系统的标准编码)。
仍然缺乏对此的认识,因为仍有许多开发人员甚至不知道编码是什么。
Mime类型有时会与编码混淆。它们是接收器识别何种数据到达的有用方式。以下是HTTP协议如何使用mime类型声明定义其内容类型的示例。
Content-Type: text/html; charset=utf-8
这是另一个混乱的重要来源。 mime类型描述了消息包含的什么类型的数据(例如text/xml
,image/png
,...)。在某些情况下,它还会另外描述如何数据编码(即charset=utf-8
)。 2点困惑:
charset=utf-8
加起来会产生语义混淆,因为如前所述,UTF-8是一种编码,而不是一种字符集。但正如前面所解释的,有些人只是交替使用这两个词。例如,在text/xml
的情况下,声明编码是毫无意义的(并且charset
参数将被简单地忽略)。相反, XML解析器通常会读取文件的第一行,查找<?xml encoding=...
标记。如果有,那么他们将使用该编码重新打开文件。
存在同样的问题when sending e-mails。电子邮件可以包含HTML消息或纯文本。同样在这种情况下,mime类型用于定义内容的类型。
但总的来说,mime类型并不足以解决问题。
对于Java(以及许多其他编程语言)以及编码的危险性,还有将字节和整数转换为字符的复杂性,因为它们的内容存储在不同的范围内。
-128
到127
)。 char
类型存储在2个无符号字节中(范围:0
- 65535
)-1
到255
的整数。如果您知道您的数据仅包含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中的快捷方式是使用读者和编写者,并在实例化时指定编码。
// 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编组器都会检查编码属性。
答案 2 :(得分:3)
字符编码是您用来解决为使用与您使用不同语言的人编写软件的问题。
您不知道角色是什么以及如何订购。因此,您不知道这种新语言中的字符串在二进制文件中会是什么样子,坦白说,您并不在意。
你所拥有的是一种将字符串从你说的语言翻译成他们所说语言的方式(比如翻译)。您现在需要一个能够以二进制形式表示两种语言且没有冲突的系统。编码就是那个系统。
它允许您编写无论语言以二进制形式表示的方式都能正常工作的软件。
答案 3 :(得分:0)
大多数计算机程序必须使用某些自然语言(人类使用的语言)与人交流。但是计算机没有表示文本的基本方法:计算机的基本表示形式是将位序列组织为字节和字,并且硬件支持将位序列解释为固定宽度的base-2(二进制)整数和浮点实数。因此,计算机程序必须具有一种将文本表示为位序列的方案。从根本上讲,这就是字符编码。没有固有的明显或正确的字符编码方案,因此存在许多可能的字符编码。
但是,实际的字符编码具有一些共有的特征。
编码后的文本分为一系列字符(字素)。
每个已知的可能字符都有编码。文本的编码由文本字符的编码顺序组成。
为每个可能的(允许的)字符分配一个唯一的无符号(非负)整数(有时称为代码点)。因此,文本被编码为无符号整数序列。不同的字符编码在允许的字符以及如何分配这些唯一整数方面有所不同。大多数字符编码都不允许许多已经存在的人类书写系统(脚本)使用的所有字符。因此,字符编码的不同之处在于它们完全可以表示哪些文本。甚至可以表示相同文本的字符编码也可以不同地表示文本,因为它们的代码点分配不同。
编码字符的无符号整数被编码为位序列。字符编码在此编码中使用的位数不同。当这些位被分组为字节时(流行的编码就是这种情况),字符编码的字节序可以不同。字符编码可以是固定宽度(每个编码字符相同的位数)还是可变宽度(某些字符使用更多位)。
因此,如果计算机程序接收到表示某些文本的字节序列,则计算机程序必须知道用于该文本的字符编码(如果要执行任何形式的操作)对该文本的操作(将其视为不透明值并将其转发不变)。唯一可能的是,文本附带其他数据,这些数据指示或程序需要使用的编码(假定),文本具有特定的编码。
类似地,如果计算机程序必须将文本发送(输出)到另一个程序或显示设备,则它必须告诉目标使用的字符编码 或程序必须使用该编码,即目标期望。
在实践中,几乎所有的字符编码问题都是由于目的地希望使用一种字符编码发送的文本,而实际上使用不同的字符编码发送的。这通常是由于计算机程序员不考虑存在许多可能的字符编码而引起的,并且他们的程序不能将编码的文本视为不透明的值,而必须从外部表示转换为输入并转换为输出的外部表示形式。