
本文探讨了字符编码背后的基本概念,然后深入研究了编码系统的技术细节。
如果您只是字符编码的基础知识,并且想更好地理解其要点,编码系统之间的差异,为什么有时有时会出现废话,以及不同编码系统体系结构背后的原理,请继续阅读。
详细了解字符编码需要大量阅读和大量时间。 我试图通过将所有内容集中到一个地方来节省您的一些工作,同时提供我认为是该主题相当全面的背景知识。
我将介绍单字节编码(ASCII,Windows-1251等)的工作方式,Unicode的历史,基于Unicode的UTF-8,UTF-16编码以及它们之间的区别,各种编码,字符编码原理以及有关如何编码和解码字符的实用指南之间的特定特征,兼容性及其不足。
尽管字符编码可能不是最前沿的话题,但了解它现在的工作方式以及过去的工作方式而又不花费大量时间是很有用的。
unicode的历史
我认为最好从计算机远不如现在这样先进或普遍的时代开始我们的故事。 当时试图制定标准的开发人员和工程师完全不知道计算机和互联网会像它们一样普及和普及。 发生这种情况时,世界需要字符编码。
但是,当计算机仅能理解一个或一个零时,又如何存储字符或字母呢? 出于这种需要,出现了第一个1字节ASCII编码,虽然不一定是第一个编码,但使用最广泛,并设定了基准。 因此,这是一个很好的使用标准。
但是什么是ASCII? ASCII码由8位组成。 一些简单的算术表明该字符集包含256个符号(八位,零和一2⁸= 256)。
集合中的前7位-128个符号(2⁷= 128)用于拉丁字母,控制字符(例如硬换行符,制表符等)和语法符号。 其他位用于本国语言。 这样,前128个字符始终是相同的,并且,如果您想对母语进行编码,则可以自己使用其余的符号。
这引起了国家编码的泛滥。 您最终遇到这样的情况:假设您在俄罗斯创建一个文本文件,默认情况下将使用Windows-1251(Windows中使用的俄语编码)。 然后您将文件发送给俄罗斯境外的某人,例如在美国。 即使收件人知道俄语,他们在计算机上打开文档(使用使用ASCII作为默认代码的文字处理软件)时也会很不走运,因为他们会看到奇怪的乱码(mojibake)而不是俄语字母。 更准确地说,任何英文字母都将很好显示,因为Windows-1251和ASCII中的前128个符号是相同的,但是只要有俄语文本,我们的收件人的文字处理软件都会使用错误的编码,除非用户手动设置了正确的字符编码。
国家字符代码标准存在的问题很明显。 最终,这些国家法规开始成倍增加,互联网开始爆炸,每个人都想用他或她的本国语言写信而不产生这些难以理解的莫吉贝克。
此时有两个选项-为每个国家/地区使用编码或创建通用字符映射表来表示地球上的所有字符。
ASCII入门
它可能看起来过于基础,但是如果我们要彻底的话,我们必须覆盖所有基础。

ASCII表中有3组列:
假设我们要用ASCII编码单词“ ok”。 字母“ o”的十六进制十进制值为111和6F。 二进制形式为-01101111。字母“ k”以十进制表示位置107,以十六进制表示6B,或以二进制表示-01101011。 因此ASCII中的“ OK”一词看起来像0110111101101011。解码过程与此相反。 我们从8位开始,将它们转换为十进制编码,然后以字符号结束,然后在表中搜索相应的符号。
统一码
从上面可以明显看出,为什么需要一个公共字符映射表。 但是会是什么样呢? 答案是Unicode,它实际上不是编码,而是字符集。 它包含1,114,112个位置或代码点,其中大多数仍为空,因此不太可能需要扩展该集合。
Unicode标准由17个平面组成,每个平面有65,536个代码点。 每个平面包含一组符号。 零平面是基本的多语言平面,我们可以在其中找到所有现代字母中最常用的字符。 第二平面包含来自死语的字符。 甚至还有两架飞机供私人使用。 大多数飞机仍然是空的。
Unicode的代码点为0到10FFFF(十六进制)。
字符以十六进制格式编码,后跟“ U +”。 因此,例如,第一基本平面包含字符U + 0000至U + FFFF(0至65,535),而块17包含U + 100000至U + 10FFFF(1,048,576至1,114,111)。
因此,现在有了一个无所不包的表,它可以对可能需要的所有符号和字符进行编码,而不是无数种编码方式。 但这并非没有缺点。 虽然每个字符以前都由一个字节编码,但现在可以使用不同数量的字节编码。 例如,您过去只需要一个字节即可对英语字母表中的所有字母进行编码。 例如,Unicode中的拉丁字母“ o”为U + 006F。 换句话说,与ASCII中的数字相同-十六进制为6F,二进制为111。 但是要对符号“ U + 103D5”(波斯数字“ 100”)进行编码,我们需要十六进制的103D5和十进制的66517,现在我们需要三个字节。
这种复杂性必须通过诸如UTF-8和UTF-16之类的Unicode编码来解决。 进一步,我们将对其进行研究。
Utf-8
UTF-8是可变宽度编码系统的Unicode编码,可用于显示任何Unicode符号。
当我们谈论可变宽度时,我们指的是什么? 首先,我们需要了解编码中的结构(原子)单元是一个字节。 可变宽度编码意味着可以使用不同数量的单位或字节对一个字符进行编码。 例如,拉丁字母编码为一个字节,西里尔字母编码为两个字节。
在继续之前,请略微谈谈ASCII和UTF之间的兼容性。
拉丁字母和按键控制字符(例如换行符,制表符等)的事实 包含一个字节可使UTF编码与ASCII兼容。 换句话说,拉丁字母和控制字符位于ASCII和UTF的完全相同的代码点中,并且都使用一个字节进行编码,因此向后兼容。
让我们使用前面的ASCII示例中的字母“ o”。 回想一下,它在ASCII表中的位置是111,或二进制形式为01101111。 在Unicode表中,它是U + 006F或01101111。由于UTF是可变宽度编码系统,因此“ o”将是一个字节。 换句话说,“ o”将在两者中以相同的方式表示。 字符0-128也是如此。因此,如果您的文档包含英文字母,则使用UTF-8,UTF-16或ASCII打开文档时不会发现任何差异,并且仅在开始工作时才会注意到差异。带有国家编码。
让我们看一下混合英语/俄语短语“ Hello world”如何在三种不同的编码系统中出现:Windows-1251(俄语编码),ISO-8859-1(西欧语言编码系统),UTF-8(Unicode) 。 这个示例之所以有用,是因为我们有两种不同语言的短语。

现在让我们考虑一下这些编码系统是如何工作的,以及如何将一行文本从一种编码转换为另一种编码,以及如果字符显示不正确,或者由于系统差异而根本无法做到这一点,会发生什么。
假设原始短语是使用Windows-1251编码编写的。 当我们查看上表时,可以看到从十进制或十六进制到十进制的转换,我们使用Windows-1251得到以下二进制编码。
01001000 01100101 01101100 01101100 01101111 00100000 11101100 11101000 11110000
因此,现在我们在Windows-1251编码中有了短语“ Hello World”。
现在,假设我们有一个文本文件,但是我们不知道文本存储在哪个编码系统中。 我们假定它是按照ISO-8859-1编码的,并使用此编码系统在我们的文字处理器中打开它。 正如我们前面所看到的,某些字符看起来很好,因为它们存在于此编码系统中,甚至在相同的代码点中,但是俄语单词“ world”中的字符效果不太好。 这些字符在编码系统中不存在,在ISO-8859-1中它们的位置或代码点中,我们发现完全不同的字符。 因此,“ m”是代码点236,“ and”是232,“ p”是240。但是在ISO-8859-1中,这些代码点分别对应于“ì”(236),“è”(232)和“ ð“(240)。
因此,在Windows-1251中编码并在ISO-8859-1中读取的混合语言短语“ Hello World”将看起来像“ Helloìèð”。 我们具有部分兼容性,我们无法在另一个系统中正确显示在一个系统中编码的短语,因为我们需要的符号根本不在第二个编码中。
我们需要Unicode编码-在本例中,我们将以UTF-8为例。 我们已经讨论过,在UTF-8中字符可以占用1到4个字节,但是另一个优点是,与两个以前的编码系统不同,UTF不限于256个符号,而是包含Unicode字符集中的所有符号。 。
它的工作方式如下:每个编码字符的第一位不对应于字形或符号本身,而是对应于特定的字节。 因此,如果第一位为零,我们知道编码的符号仅使用一个字节-这使得该集合向后兼容ASCII。 如果我们仔细查看ASCII符号表,会发现前128个符号(英文字母,控制字符和标点符号)以二进制表示,它们都以0开头(请注意,如果将字符转换为使用在线转换器或任何类似的二进制文件时,可能会丢弃前零个高阶位,这可能会造成混淆。
01001000-第一位值为0,因此1个字节编码1个字符->“ H”。
01100101-第一位值为0,因此1个字节编码1个字符->“ e”。
如果第一位的值不为零,则该符号将被编码为几个字节。
两字节编码的前三个比特值将为110。
11010000 10111100-标记位是110和10,因此我们使用2个字节来编码1个字符。 在这种情况下,第二个字节始终以“ 10”开头。因此,我们忽略控制位(以红色和绿色突出显示的前导位),然后查看代码的其余部分(10000111100),并转换为十六进制(043) -> U + 043C,用Unicode给我们俄语“ m”。
三字节字符的起始位是1110。
11101000 10000111 101010101-我们将除控制位之外的所有位加起来,发现以十六进制表示的是103B5,U + 103D5-古代波斯数字一百(10000001111010101)。
四字节字符编码从前导位11110开始。
11110100 10001111 10111111 10111111-U + 10FFFF是Unicode集(10000111111111111111111)中的最后一个可用字符。
现在,我们可以轻松地以UTF-8编码编写我们的多语言短语。
UTF-16
UTF-16是另一种可变宽度编码。 UTF-16和UTF-8之间的主要区别在于,UTF-16每个代码单元使用2个字节(16位),而不是1个再见(8位)。 换句话说,以UTF-16编码的任何Unicode字符都可以是两个或四个字节。 为简单起见,我将这两个字节称为代码单元。 因此,在UTF-16中,任何字符都可以使用一个或两个代码单元表示。
让我们从使用一个代码单元编码的符号开始。 我们可以很容易地计算出,一个代码单元有65,535(216)个字符,完全与Unicode的基本多语言平面对齐。 在UTF-16中,该平面中的所有字符将由一个代码单位(两个字节)表示。
拉丁字母“ o”-00000000 01101111。
西里尔字母“ M”-00000100 00011100。
现在让我们考虑基本多语言平面之外的字符。 它们需要两个代码单元(4个字节),并以稍微复杂一些的方式进行编码。
首先,我们需要定义代理对的概念。 代理对是用于编码单个字符(共4个字节)的两个代码单元。 Unicode字符集为代理对保留了D800到DFFF的特殊范围。 这意味着当将一个代理对转换为十六进制的字节时,我们最终得到一个在该范围内的代码点,它是一个代理对而不是一个单独的字符。
要对10000-10FFFF范围内的符号进行编码(即,需要多个代码单元的字符),请按以下步骤操作:
从代码点减去10000(十六进制)(这是10000-10FFFF范围内的最低代码点)。
我们最终得到一个不超过FFFF的20位数字。
我们最终得到的高10位被添加到D800(Unicode代理对范围中的最低代码点)。
接下来的10位被添加到DC00(也来自代理对范围)。
接下来,我们得到2个代理16位代码单元,其中的前6位将单元定义为代理对的一部分。
每个代理中的第十个位定义了对的顺序。 如果我们有一个“ 1”,那就是领先或较高的代理;如果我们有一个“ 0”,则是落后或较低的代理。
通过以下示例进行说明时,这会更有意义。
让我们编码然后解码波斯数字一百(U + 103D5):
103D5-10000 = 3D5。
3D5 = 0000000000 1111010101(高10位为零,当转换为十六进制时,我们以“ 0”(前十个)和3D5(后十个)结束)。
0 + D800 = D800(1101100000000000)的前6位告诉我们,此代码点落在代理对范围内,第十个位(从右侧)具有“ 0”值,因此这是高代理。
3D5 + DC00 = DFD5(1101111111010101)的前6位告诉我们,此代码点落在代理对范围内,第十个位(从右侧)为“ 1”,因此我们知道这是低代理。
以UTF-16编码的结果字符看起来像-1101100000000000 1101111111010101。
现在让我们解码字符。 假设我们有以下代码点-1101100000100010 1101111010001000:
我们将其转换为十六进制= D822 DE88(两个代码点都在代理对范围内,因此我们知道我们正在处理代理对)。
1101100000100010-第十位(从右起)是“ 0”,因此这是高代理。
1101111010001000-第十位(从右起)是“ 1”,因此这是低位替代。
我们忽略了6位,将其标识为代理,并保留了0000100010 1010001000(8A88)。
我们加上10000(代理范围内的最低代码点)8A88 + 10000 = 18A88。
我们看一下U + 18A88 = Tangut Component-649的Unicode表。
感谢阅读本文的所有人!
我希望这能为您提供丰富的信息,同时又不让您感到无聊。
您可能还会发现有用:
Unicode字符集
内容本地化策略:基于IP或基于浏览器
关于翻译
Alconost是为70多种语言的应用程序 , 游戏 ,视频和网站提供本地化服务的全球提供商。 我们提供由母语为母语的语言学家,语言测试,基于云的工作流,连续本地化,24/7项目管理以及任何格式的字符串资源提供的翻译。 我们还为Google Play和App Store制作广告和教育视频和图像,预告片,解释器和预告片。