计算机编码

计算机编码

参考文章

ASCII

文件的十六进制显示:每两个(有些是四个)十六进制数摆一起。每位十六进制数可以转换为四位二进制数,如十六进制A转化成二进制为1010,而一个字节是8位,故一个字节=两个二进制数。一个字节的前四位和后四位要拼接在一起得到的值才是ascii中字符对应的十进制码。每个字节的八位中,前四位为高四位,后四位为低四位

在计算机中,我们将某个字符存储在计算机硬件中的二进制码每四位转换成一个十六进制数而得到的十六进制码称为该字符的“十六进制码”,将某个字符在ascii码表中的十进制码简称为该字符的“十进制码”。十六进制码到十进制的计算转换就是常规的x*(16**1)+y*(16**0)这样,如上面0x50可以直接通过这样计算得到其十进制码:5*16+2*0=80。

如0x50:高四位十六进制是5,转化成二进制为0101,低四位十六进制是0,在ascii中查找十六进制码为50的字符,就可以找到字符P。转化成二进制是0000,因此这个字节以二进制显示则是0b01010000(实际存储在计算机硬件中的形式)。转化成十进制,即2**6+2**4=80,在ASCII码中查找十进制码为80的字符,就能找到字符P,因此这个字节表示的字符是P。

以上所说都是以一个字节表示一个字符,最早设计计算机时,科学家们也只想用一个字节表示计算机所需符号,此外统一规定第一位为0(后面会说到第一位为1有其他用处),则一个字节最大为0b01111111,可表示范围为0b00000000-0b01111111,即可以装下128个符号,这就是ASCII码表,如下图。

那么问题来了,只使用一个字节表示符号,只能显示ASCII的字符,很多其他字符,如中文、法语、日语等无法显示,其他非英语国家的语言文字也无法显示,于是就出现了用两个字节,三个字节来表示一个字符,即把字符码映射表的十进制码从128拓展到了65536(两个字节)或者更高,于是出现了Unicode码。Unicode的学名是"Universal Multiple-Octet Coded Character Set",简称为UCS。UCS可以看作是"Unicode Character Set"的缩写。

那么又有问题了,如果将所有字符都用Unicode码存储,则要统一长度,如都用4个字节来存储,低于四个字节的码在前面用0填充,但是这种方式极其浪费存储空间,于是需要一种可变长度的存储方式来存储,当出现一个字节时,根据一些字节标识来识别这个字节是一个字节表示的字符,还是多个字节表示的字符中的其中一个字节?

UTF-8

参考文章

针对这个问题,就要提到编码方式。编码方式是指字符的Unicode二进制码在计算机硬件中是如何存储的。最常用的编码方式就是UTF-8(UTF:Unicode Transformation Format)。注意,ASCII和Unicode都是字符集,即不同字符对应的二进制码、十进制码、十六进制码是多少,是一种类似于映射表的集合,它并没有规定二进制码在存储硬件中是如何编排的。而UTF-8则是一种编码方式,编码方式是一种规则,它规定了Unicode中的字符在存储硬件中编排方式。UTF-8编码方式的规则如下:

  • 对于单字节的符号,字节的第一位设为0,后面7位为这个符号的Unicode码。因此对于ASCII中的字符,UTF-8编码和ASCII码是相同的。
  • 对于n字节的符号(n > 1),第一个字节的前n位都设为1,第n + 1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的Unicode码。
UNICODE UTF-8 可容纳位数
00000000 - 0000007F 0xxxxxxx 7
00000080 - 000007FF 110xxxxx 10xxxxxx 11
00000800 - 0000FFFF 1110xxxx 10xxxxxx 10xxxxxx 16
00010000 - 001FFFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 21
00200000 - 03FFFFFF 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 26
04000000 - 7FFFFFFF 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 31
下面,还是以汉字“严”为例,演示如何实现UTF-8编码。

严的Unicode是4E25(0100 1110 0010 0101),根据上表,可以发现4E25处在第三行的范围内(0000 0800 - 0000 FFFF),因此严的UTF-8编码需要三个字节,即格式是1110xxxx 10xxxxxx 10xxxxxx。然后,从严的最后一个二进制位开始,依次从后向前填入格式中的x,多出的位补0。这样就得到了,严的UTF-8编码是11100100 10111000 10100101,转换成十六进制就是E4B8A5。因此可以发现Unicode中的字符(前面ASCII的部分除外),其Unicode十六进制码和其UTF-8十六进制码是不一样的。Unicode码指该字符在Unicode码表中的索引,UTF-8码是指将该字符的Unicode码按照UTF-8编码方式生成的码,多了一些用于识别字节数的前缀。因此,编码的工作就是,按照Unicode码表获取字符对应的Unicode码,按照不同的编码方式将Unicode码加工成对应的码;解码的工作是,将获取到的码,按照给定的方式解析成Unicode码,再查找Unicode码表转换成所需要的字符集。我们常常打开一个文件后出现乱码,就是因为这个文件是用编码规则A编码的,再存储到计算机硬盘中,但是打开时用了编码方式B去解析该文件,于是就显示乱码。

UTF-8编码方式就解决了不同字符需要的字节数不同的问题和在一定程度上提高了不同字节数字符在计算机存储中的空间利用率问题。

字节序

参考文章

字节序的定义如下(来源于wiki):

字节顺序,又称端序或尾序(英语:Endianness),在计算机科学领域中,指电脑内存中或在数字通信链路中,组成多字节的字的字节的排列顺序。

对于多字节字符,按照其字节的原始排列顺序(如字符严的Unicode码为4E25,则4E25就是其原始排列顺序)排列后,先读到的那个或那两个字节为高位字节,后读到的为低位字节

字节的排列方式有以下两种:

  • 高位字节在前,低位字节在后,称为“高位优先”(big-endian),和原始排列方式一致;
  • 高位字节在后,低位字节在前,称为“低位优先”(little-endian),和原始排列方式反转。

但是这样的话,计算机在读取和写入数据时就必须先知道该文件的字节序。针对这个问题,Unicode规范定义,每一个文件的最前面分别加入一个表示编码顺序的字符,这个字符的名字叫做"零宽度非换行空格"(zero width no-break space)。

  • UTF-16编码后大部分字符都是两个字节,少数是4个字节。在UTF-16中,标识字节顺序的符号为FEFF或FFFE,以FE FF开头,表示该文件采用big-endian方式存储,以FF FE开头,则采用little-endian方式
  • UTF-8编码方式编码的字节是变长的,由第一个字节来确定该字符的字节数,所以没有高低位字节顺序的说法,所以UTF-8不需要表示字节顺序的字符,但是微软的记事本如果以默认的UTF-8编码时,是带有这个顺序标识字符的,该标识字符的字节为EF BB BF,称之为BOM(Byte-Order Mark),但BOM并不是必须的,只是微软的Windows有这个习惯。其他平台如Linux不用BOM,因此若需要跨平台使用,建议不带BOM。此外,在生成csv文件时,如果以UTF-8 without BOM保存文件时打开后会乱码,微软csv默认要带BOM,txt文件没有这种问题。
  • UTF-32是定长编码方式,每个字符都使用4个字节编码,因此在UTF-32中,标识字节顺序的符号为0000FEFF(big-endian)或FFFE0000(little-endian)。
posted @ 2020-10-18 11:26  yury757  阅读(545)  评论(0)    收藏  举报