本文旨在汇集和分解文本编码的工作原理和机制,详细介绍这种分解和解释的机制。 对于仅大致想象什么文本编码是什么以及它们如何工作,它们如何彼此不同,为什么有时会出现不可读字符,不同编码具有什么编码原理的人们将很有用。
要对这个问题有详细的了解,您必须阅读并收集多篇文章,并在此上花费大量时间。 在本材料中,将所有内容放在一起,从理论上讲,应该节省时间,我认为分析非常详细。
切入会发生什么:单字节编码(ASCII,Windows-1251等)的操作原理,Unicode出现的先决条件,什么是Unicode,Unicode编码UTF-8,UTF-16,它们的区别,基本功能,兼容性以及不同编码的不兼容,字符编码的原理以及编码和解码的实际分析。
当然,与编码有关的问题已经失去了相关性,但是我仍然认为了解它们现在如何工作以及以前如何工作并不是多余的。
Unicode先决条件
我认为值得从计算机化还不那么发达,才刚刚兴起的时代开始。 然后,开发人员和标准化人员不认为计算机和Internet会获得如此巨大的普及和普及。 实际上,随后就需要对文本进行编码。 必须以哪种形式将字母存储在计算机中,并且他(计算机)只能理解一和零。 因此开发了一个单字节ASCII编码(很可能不是第一个编码,但是它是最常见和最有指示性的,因此我们将其视为参考)。 她是什么样的人? 此编码中的每个字符都用8位编码。 很容易计算出,基于此,编码可以包含256个字符(8位,零或1 2
8 = 256)。
该编码的前7位(128个字符2
7 = 128)被分配给拉丁字符,控制字符(例如换行符,制表符等)和语法字符。 其余的保留给本国语言。 也就是说,原来的前128个字符始终是相同的,并且,如果您想对母语进行编码,请使用剩余的容量。 实际上,出现了一个巨大的国家代码动物园。 现在您自己可以想象,例如,当我在俄罗斯时,我会创建并创建一个文本文档,默认情况下,该文档是使用Windows-1251编码(Windows中使用的俄语编码)创建的,并发送给了例如美国的某人。 甚至我的对话者知道俄语的事实也无济于事,因为当他在计算机上打开我的文档时(在编辑器中使用相同ASCII的默认编码),他不会看到俄语字母,而是看到krakozyabry。 更准确地说,我用英语编写的文档中的那些位置将显示出来没有问题,因为Windows-1251和ASCII编码的前128个字符是相同的,但是如果不是在编辑器中指示正确的编码,则是在我编写俄语文本的地方,以鳄鱼的形式。
我认为国家编码的问题是可以理解的。 实际上,这些国家/地区编码有很多,并且互联网已经变得非常广泛,其中的每个人都想用自己的语言书写,并且不希望自己的语言看起来像弯曲的头发。 有两种解决方法,分别为编码的每一页指示或为世界符号表中的所有字符创建一个通用符号。 第二个选项获胜,因此创建了Unicode字符表。
ASCII小作坊
这似乎很基本,但是由于我决定详细解释所有内容,因此这是必要的。
这是ASCII字符表:

这里我们有3列:
- 十进制字符数
- 十六进制格式的字符数
- 符号本身的表示形式。
因此,对字符串“ ok”(ASCII)进行编码。 字符“ o”(英语)的位置在十进制中为111,在十六进制中为
6F 。
01101111
将其转换为二进制系统
01101111
。 符号“ k”(英语)-以十进制表示的位置107和以十六进制表示的
6B表示二进制
01101011
。 以ASCII编码的总字符串“ ok”将如下所示
01101111 01101011
。 解码过程将是相反的。 我们使用8位,将它们转换为10位十进制编码,获取字符号,查看表格中的字符是什么类型。
统一码
在为所有字符世界创建公用表的前提条件下,进行整理。 现在,实际上是桌子本身。 Unicode-这是表(这不是编码,而是符号表)。 它包含1,114,112个职位。 这些位置大多数尚未用符号填充,因此不太可能需要扩展此空间。
该总空间分为17个块,每个块65,536个字符。 每个块包含其自己的字符组。 零块是最基本的零块,它包含所有现代字母中使用最多的字符。 在第二块中,是已绝种语言的字符。 有两个保留供私人使用的块。 大多数块尚未填充。
Unicode字符的总容量为
0到
10FFFF (十六进制)。
十六进制字符写有前缀“ U +”。 例如,第一个基本块包括从U + 0000到U + FFFF(从0到65,535)的字符,而最后的第17个块包括从U + 100,000到U + 10FFFF(从1,048,576到1,114,111)的字符。
好了,现在,我们有了一个综合的表格,而不是国家编码的动物园,在其中加密了所有可能对我们有用的字符。 但是也有缺点。 如果以前每个字符都用一个字节编码,那么现在可以用不同数量的字节编码。 例如,要对英语字母的所有字符进行编码,一个字节仍然足够,例如,英语中相同的“ o”字符是unicode U + 006F,即与ASCII中相同的数字是十六进制为
6F ,十进制为111。 但是要对字符“
U + 103D5 ”(这是古代的波斯数字一百)进行编码-十六进制为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是可变长度编码,因此此字符将被编码在其中一个字节中。 即,在两种编码中该符号的表示将是相同的。 因此,对于从0到128的整个字符范围。也就是说,如果您的文档由英文文本组成,那么如果您以UTF-8和UTF-16和ASCII编码打开它(例如,在UTF-16中,这些字符都是同样地,它们将以两个字节进行编码,因此,如果您的编辑器忽略零字节,您将看不到差异),依此类推,直到开始使用国家字母。
让我们在实践中比较三种不同的编码形式“ Hello World”的外观:Windows-1251(俄语编码),ISO-8859-1(西欧语言编码),UTF-8(统一编码)。 该示例的实质是该短语用两种语言编写。 让我们看看它在不同编码下的外观。
在编码ISO-8859-1中,没有这样的字符“ m”,“ and”和“ p”。现在,让我们使用编码,看看如何将字符串从一种编码转换为另一种,以及如果转换错误或由于编码差异而无法完成转换将发生什么。
我们假定该短语最初是在Windows-1251中编码的。 根据上表,我们以Windows-1251编码的二进制形式编写此短语。 为此,我们只需要将符号从二进制转换为十进制或十六进制(来自上表)。
01001000 01100101 01101100 01101100 01101111 00100000 11101100 11101000 11110000
好吧,这是Windows-1251中编码的短语“ Hello World”。现在,假设您有一个包含文本的文件,但是不知道该文本的编码方式。 您假定它已在ISO-8859-1中进行了编码,并以该编码在编辑器中将其打开。 如上所述,对于一部分符号而言,一切都是有序的,它们采用这种编码,甚至位于相同的位置,但是对于“世界”一词中的符号而言,则一切都变得更加复杂。 这些字符不在此编码中,而在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。 我们已经发现,其中的字符可以使用从1到4的不同字节数进行编码的事实。 现在值得一说的是,使用UTF不仅可以像前两个一样编码256个字符,而且可以对所有Unicode字符进行编码
它的工作原理如下。 编码字符每个字节的第一位不负责字符本身,而是负责确定字节。 也就是说,例如,如果前导(第一)位为零,则意味着仅一个字节用于编码字符。 提供与ASCII的兼容性。 如果仔细查看ASCII字符表,您会发现如果前128个字符(英文字母,控制字符和标点符号)转换为二进制,则所有内容都以零位开头(请注意,如果您使用例如在线方式将字符转换为二进制系统,则应格外小心)转换器,则可以丢弃第一个零前导位,这可能会造成混淆)。
01001000
第一位为零,然后1个字节编码1个字符->“ H”
01100101
第一位为零,表示1个字节编码1个字符->“ e”
如果第一位不为零,则将字符编码为几个字节。
对于双字节字符,前三位应为-110
110 10000 10 111100
在110的开头,然后2个字节编码1个字符。 在这种情况下,第二个字节始终以10开头。总共,丢弃控制位(初始的控制位,以红色和绿色突出显示),并取走所有剩余的
10000111100
(
10000111100
),将它们转换为十六进制(043C)-> U + 043C(在Unicode中,符号“ m” ”。
对于第一个字节中的三字节字符,前导位是1110
1110 1000 10 000111 10 1010101
我们将除控制位以外的所有内容相加,得出十六进制为103V5,U + 103D5为古代波斯数字一百(
10000001111010101
)
对于第一个字节中的四字节字符,前导位是11110
11110 100 10 001111 10 111111 10 111111
-U + 10FFFF是unicode表中的最后一个有效字符(
100001111111111111111
)
现在,如果需要,我们可以用UTF-8编码记录我们的短语。
UTF-16
UTF-16也是可变长度编码。 它与UTF-8的主要区别在于其中的结构单元不是一个字节而是两个字节。 也就是说,在UTF-16编码中,任何Unicode字符都可以用两个或四个字节编码。 为了清楚起见,让我将一对这样的字节称为代码对。 基于此,以UTF-16编码的任何Unicode字符都可以使用一个或两个代码对进行编码。
让我们从一个代码对编码的字符开始。 很容易计算出可以有65,535个这样的字符(2v16),这与基本Unicode块完全一致。 此Unicode块中采用UTF-16编码的所有字符都将使用一个代码对(两个字节)进行编码,这里的一切都很简单。
符号“ o”(拉丁文)
00000000 01101111
符号“ M”(西里尔字母)
00000100 00011100
现在考虑基本Unicode范围之外的字符。 对于它们的编码,需要两个代码对(4个字节)。 而且编码它们的机制要复杂一些,让我们按顺序进行。
首先,我们介绍代理对的概念。 代理对是用于编码一个字符(共4个字节)的两个代码对。 对于此类代理对,在Unicode表中分配了从
D800到
DFFF的特殊范围。 这意味着当将代码对从字节格式转换为十六进制时,您会从该范围中获得一个数字,那么这不是一个独立字符,而是一个代理对。
要编码
10000-10FFFF范围内的字符(即,一个字符,您需要使用多个代码对),您需要:
- 从字符代码中减去10000 (十六进制)(这是10000-10FFFF范围内的最小数字)
- 作为第一点的结果,将获得不大于FFFFF的数字,最多占用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 ( 110110
110110 0 000000000
)前6位确定代理对范围内的数字第十位(右侧)为零,则这是第一个代理 - 3D5 + DC00 = DFD5 (
110111 1 111010101
)的前6位确定代理对范围内的数字是第十个位(右侧)是1,则这是第二个代理 - 此字符在UTF-16中的
1101100000000000 1101111111010101
为1101100000000000 1101111111010101
现在解码相反。 假设我们有这样的代码-1101100000100010 1101111010001000:
- 转换为十六进制形式= D822 DE88 (这两个值均来自代理对的范围,因此我们前面有一个代理对)
110110 0 000100010
第十位(右侧)为零,则第一个替代110111 1 010001000
第十位(右侧)为1,然后为第二个替代- 我们丢弃负责确定代理人的6位,得到
0000100010 1010001000
( 8A88 ) - 加10,000 (较少的替代范围) 8A88 + 10000 = 18A88
- 在unicode表中,查看字符U + 18A88 = Tangut Component-649。 Tangut脚本的组件。
感谢那些能够读到最后的人,我希望它是有用的并且不会很无聊。
这是有关此主题的一些有趣链接:
habr.com/en/post/158895-有关编码的有用常规信息
habr.com/en/post/312642-关于Unicode
unicode-table.com/ru-Unicode字符表本身
好吧,实际上没有她你会在哪里
zh.wikipedia.org/wiki/%D0%AE%D0%BD%D0%B8%D0%BA%D0%BE%D0%B4-Unicodeen.wikipedia.org/wiki/ASCII-ASCIIzh.wikipedia.org/wiki/UTF-8-UTF-8zh.wikipedia.org/wiki/UTF-16-UTF-16