字符编码之编码格式

写作目的

        我曾经在校期间不管是学习编程涉及到字符串或者读写文件操作还是进行另保存文本文件时,时常会碰到对字符或者字符串进行处理的问题。我当时是知其然而不知所以然,所以在进行编程作业的过程中,往往花费较多精力去解决关于字符编码的问题(比如乱码),这为后来工作当中带来了极大的不便。最终楼主决定花点时间专门研究一下字符编码的问题,以免将来再次遇到这种情况时会稀里糊涂而浪费时间。

     因此,写此文主要目的是梳理以前学到的东西,并且总结关键的知识点,再进一步对此加深理解。同时分享出来,也希望能够帮助诸位小伙伴。--开始写于2019.1月

本文概述

        原本打算鼓励自己多阅读一些相关文献,就觉得已经足够了。然而,事实并非如此,这并不是说他人写得不够好,而是因为楼主从大量的文章中获取到的内容比较零散,再加上一些文章叙述的文字过多并且过于详细但又不够全面,又缺少借助演示图来理解。其实最主要的原因是没有整体性的知识点架构,这很容易扰乱楼主的思路,这显然不是一种好的学习方式。因此写本文之前,楼主在大佬们文章的基础上,同时参考了一些相关的书籍,去除了一些不必要的编码发展历史以及不必要过早解释的东西(实践性很强且没有一定开发经验是很难理解的知识),以楼主的理解抓取主要的知识点整理出来,为避免出现学习之后又忘记的尴尬局面。

        本文会从其发展的阶段来讲解各种常见的编码格式。如果本文尚未涉及到其他编码格式,请另行研究。

正文

二进制文件和文本文件

          在讲编码之前,我们首先得要弄懂二进制文件和文本文件的区别。我们时常编译一个程序会生成一个二进制映射文件,比如后缀名为.bin或者把项目生成DLL文件包含了.lib.dll,此类文件一般是打不开的,如果用记事本打开,其内容就是一堆乱码,除非试图使用特定的工具,打开之后就是一大堆2进制或16进制的数据,这类文件就叫做二进制文件。而文本文件就是我们时常用到的以ASCII码形式或者其他编码格式来存储数据并可以运用软件打开查看内容的文件,比如txt文件,或者我正在写这篇文章用的doc文件。

  存储的数据类型:二进制文件存储数据类型为图形文件(下面讲到的BMP格式文件就是图形文件)以及其他非字符型数据等(二进制映射文件)

 1.二进制文件是变长编码,也可以看成是值编码,它完全可以由编辑者指定的位数来确定一个实型数或者其他信息。(其他信息有用的信息用指定的二进制数据表示,如BMP文件里面的某些二进制数指定的是BMP头文件,又有一些二进制数指定的是头文件长度)

2. 而文本文件是定长编码,即固定位数为一个字符的编码,下文会详细讲到这些。文本文件是根据指定的编码格式转换为二进制数据存储在磁盘上,读取时,根据原来指定的编码格式转换为相应的字符显示出来。这就说明了我们另保存记事本时为何有编码的选项,就是这种原因。

         假如二进制文件的内容里面如果有数字10,二进制数表示10,可能只占用2位数(当然可由编辑者灵活指定位数),那么其存储到磁盘里就是10,直接读取出来的可能还是10。然而由于二进制文件的数据位数完全是由于编辑者决定的,数字10存储的样子又不一定就是数字10.

         假如以ASCII码形式文本文件的内容如果有数字10,则二进制数需要对照ASCII码表来转换成00110001 00110000(数字1十进制为49,数字0十进制为48),各自占用8位,共16位数,那么其存储到磁盘里的是00110001 00110000,读取出来是还要把00110001 00110000根据编码格式转回为12. 文本文件是按照编码规范进行存储的,而二进制文件就是由编辑者决定的(比如说编译器作为编辑者,决定哪些数字符号用哪些二进制数据表示以及多少个字节).

 二进制文件的优点:

1.由于不用转换格式而存取速度快。

二进制文件是无格式存储起来的,意思是不同于文本文件必要使用编码格式。文本文件需要转换过程,从磁盘读取数据时,先把二进制数据根据编码格式转换为指定的字符。写字符存到磁盘时,先把字符根据编码格式转换为指定的二进制数据。

2.不用按照编码格式指定一个字符占用的字节而占用内存的空间小。

3.存储数据灵活。

缺点:

但是由于其存储的灵活度比文本文件高,所以增加了译码的难度。

什么叫编码?

        在计算机里,所有数据进行存储和运算时都要使用二进制数来表示的。而当我们进行软件开发涉及字符处理或者文本编辑时,往往需要使用哪种数据对应指定的哪些二进制数字来表示的,例如字符"A"ASCII码指定用"65"十进制(二进制为0100 0001)来表示,这就是编码。

      二进制文件和文本文件都是属于编码。不同的是,文本文件有编码格式,而二进制文件没有。下面就开始讲解编码格式。

ASCII

        ASCIIAmerican Standard Code for Information Interchange,美国信息交换标准代码)。

如果我们完全按照个人的想法进行编码,那么当大家互相使用时就会造成编码混乱,毕竟那是因为每个人都有各自的一套编码约定。例如你把字符"A""65"十进制表示,而张三把字符"A""66"十进制表示,当你把张三编辑的文本在你的编码环境里打开,就出现了乱码的情况。所以,为了解决这个问题,大家就必须使用相同的编码约定,所以在以上例子中,张三必须要把字符”A””65”十进制表示,于是就有了编码格式,统一规定哪些字符是用哪些二进制数来表示的,每个人都要遵从这个规则。例如ASCII码。 
        ASCII
码使用了指定的6位、7位或8位二进制数来组合出64128256种可能的字符。由于考虑到可靠性,不能使用转换字符,因此6位就排除掉了。又考虑到成本价格问题,8位也就排除掉了,所以这也就解释了一个字节8位中的最高位为0(即下一段讲到的高四位中的最高位)的原因,一共有7ASCII码。

一个字节对应8位二进制数,分成高4位和低4位。高4位表示ASCII控制字符和ASCII打印字符这两个字符种类,低4位表示各个字符(数字、大小写字母,符号等)。高4位的最高位(即第8位,二进制数的最左边的位,高4位的最高位)通常为0  


ASCII存储格式

字符编码之编码格式_第1张图片

 

ASCII码优点

方便性:

例如26个字母是在ASCII码中的位置是连续的,只要将高四位中的某一位把0翻转成1,或者把1翻转成0,就可以获得相应的大小写字母。

例如数字0~9在低4位二进制相应的代码顺序是0001~1001,即归类按顺序来表示的,为的是方便记忆。

普及性:

ASCII码是一种极其可靠的标准,没有其他哪一种编码格式能像ASCII码一样普及,它扎根于我们计算机的键盘、视频显示器、系统硬件、打印机、字体文件,操作系统和因特网。

ASCII码缺点

       由于ASCII码是美国标准,只能满足美国的要求,所以它并不能满足其他英语国家(更不用说东亚地区的国家)的需要。比如其他英语国家的一些特殊符号例如重音符号,在ASCII码是没有的。

ANSI

       为了解决各国的编码需求,ANSI码由此产生了。ANSI编码使用0x000x7F(十进制0127)范围的1个字节来遵从ASCII码的规则。超出的0x800xFFFF范围的2个字节来表示1个字符来表示其他语言的字符,即扩展的ASCII编码。由于2个字节最多可以存储字符数是216次方(65535个字符),对于其他一个语言足够了。ASCII码之后的扩张编码就是由其他编码决定,例如中国GB2312编码作为ASCII码的扩展,其他国家语言也一样。而对于一些特殊的文字,还需要3~4个字节表示。

ANSI存储格式

         如图所示,在GBK编码表当中,查找字在[0xD6,0xD0]的位置存储了2个字节;其中第一个字节”0XD6”是在8位表示1个字节中,128ASCII码之后剩下的128个代码(这里128个代码是ASCII的扩展,范围在[0x80~0xFF]),0XD6在汉码区的范围内。第二个字节的位置0XD0,可以根据第一个字节的汉码区的代码,查找相应的汉字。

该图来自于一个查询GBK格式汉字的网站。想要加深理解的小伙伴可以专找其他文字进行对照一下。

字符编码之编码格式_第2张图片

根据网上所讲的位元组问题,由于某些文章仅仅用文字描述,可能导致一些小伙伴也难以理解,那么下面楼主制作了一张图,以便助于大家理解。

由于8位表示一个字节,共有256个代码,那么ASCII码是由最初128个代码组成的,而较高的128个代码是ASCII码的扩展,某些文章所讲的首位元组其实就是ASCII码的扩展,而跟随位元组就是第二个字节开始的代码。所以1个象形文字=首位元组+跟随位元组。

字符编码之编码格式_第3张图片

注意:ANSI是一种编码格式,并不是ASCII码的扩展。比如说”2B,其中”2B”,在ANSI编码当中会以ASCII码形式进行表示并存储,只占用1个字节;而ASCII码的扩展+跟随位元组所组成的1个字符。


ANSI优点

ANSI解决了ASCII码扩展的问题,使其他国家可以在各自的操作系统环境里拥有自己的一套字符集。

ANSI缺点

      局限性:不同国家和地区制订了不同的标准,正是如此,不同ANSI编码之间是互不兼容的。当信息在国际间交流时,无法将两种或者两种语言以上的文字,存储在同一段ANSI编码的文本中。也就是说,ANSI只能存储ASCII码和ASCII扩展的其他国家指定的某种语言,并不能把两个或者两个以上的国家或地区的语言融合在一起。比如ASCII码的扩展要么是简体中文,要么是繁体汉字,两者不可兼容在一起,不可共享同一段ANSI编码。

所以,对于ASCII码的扩展,针对某些国家使用字符集时需要相应的操作系统环境。比如在简体中文windows操作系统中,ANSI编码代表GBK编码;而在日文windows操作系统中,ANSI编码代表Shift_JIS编码。这些都可称为本地编码。

DBCS

      DBCS(double byte character set,双字节字符集)

为了融合其他国家语言(中文、日文、韩文)而同时保持与ASCII有着某种程度的兼容性,于是就出现了双字节字符集。双字节字符集前面128个代码就是ASCII码。前128个代码占有一个字节,而对于一个复杂的象形文字需要这128个代码的同时还要跟随有第二个字节,与ANSI的规则一样。这两个字节分为前导字节和尾随字节,代表一个单独的字符。虽然中文汉字、日文和韩文共享许多相同的象形文字,但毕竟这些语言是不同的。于是就有了四个不同的双位元组字元集:内码表932(日文)949(韩语)936(简体中文)950(繁体汉字)。DBCS只有在为这些国家制作的windows版本才被支持。 

DBCS存储格式

字符编码之编码格式_第4张图片

DBCS存储格式图中,由于象形文字“栋”字占用2个字节,属于一个2个字节存储的字符集;其前面“2”、“B”各占用1个字节,是另一个1个字节存储的字符集。使用DBCS编码格式来存储“2B栋”,若要指针寻找“栋”字时,马上用指针指向图中的“1011 0000”时是无法判断该字节是一个1字节字符集,还是2字节字符集。这个需要回到字符串的首处(即“2”)开始进行解析(解析是指“0011 001”转换成“2”),该指针随着编码解析直到“栋”字的位置时,才会知道“1011 0000”是何物。

DBCS的优点

解决了ANSI编码的缺点,对各个国家的语言之间划分了内码区域,可以实现各个语言的信息交流。

 

DBCS的缺点

       有些字符(特别是ASCII)是由一个字节组成的,便会产生奇怪的编程问题。例如,一个字符串长度不能因为字节数量而决定。字符串的长度需要解析之后方能判断,每个字节都要被检查是不是双字节的前导字节。如果你有一个指针指向一个DBCS字符串的中间,那前一个字符的地址是什么呢?常规做法是回到字符串的开始,一直解析到指针的位置。那么这样做便会比较麻烦。

     由于1个字符串中的每个字符都由1个或2个字节组成,所以某些字符有1个字节宽,而有的字符有2个字节宽。如果是一个汉字,我们无法用指针在解析之前去判断此字符是占用1个字节还是2个字节。

Unicode

        Unicode在百度百科上解释为统一码、万国码、单一码,顾名思义就是Unicode可以容纳世界上所有文字和符号的字符编码方案,即它能解决各国不同的语言以及进行文本转换、处理字符要求的问题。

Unicode只有一个字符集,那么何为说是“只有一个字符集”呢?楼主在之前介绍的DBCS是双字节字符集,可以说是2个字符集,即如果是字母数字用ASCII码表示,就占用了一个字节,为一个字符集。而对于象形文字是ASCII码扩展+尾随字节,占用了两个字节,为另一个字符集,总共2个字符集。Unicode无论是纯数字字符还是象形文字都占用2个字节,只为一个字符集。其中,中、日、韩的三种文字占用了Unicode0x30000x9FFF的部分。

       DBCS码中,一个象形文字是利用前导字节+尾随字节组成一个字符的,占用2个字节。但由于这并不是按编码顺序的固定长度来表示一种语言的字符的,处理DBCS字符串就会很杂乱,比如在前面提到的指针指向的位置问题,需要回到字符串de开始处解析之后,指针才能确定某字符的位置,比较麻烦。于是为了解决这个问题,便出现了Unicode码,处理Unicode码是有秩序长度固定的。何为“有秩序长度固定”呢?当你了解上文的内容便会知道,Unicode这一特点就是指无论某字符是哪种类型的,都必须占用2个字节,字符位置有序长度固定。

      Unicode其实就是使用UTF-16编码格式的。UTF-16全称就是Unicode Transformation Format(Unicode转换格式)

Unicode的存储格式

字符编码之编码格式_第5张图片

 

Unicode缺点

      Unicode不管是ASCII字符还是ASCII的扩展符,都占用两个字节,这导致对于只使用ASCII码就会造成浪费内存空间。

UTF-8编码

UTF-88-bit Unicode Transformation Format8Unicode转换格式)是一种针对Unicode的可变长度字符编码,又称万国码。

特点:UTF-8将一些字符编码为1个字节,一些字符编码为2个字节,另一些字符编码为3个节,还有一些是4个字节。值在0x0080以下的字符为1个字节,即ASCII码,这对美国的语言合适。0x00800x07FF之间的字符为2个字节,这对欧洲和中东地区的语言合适。0x0800以上的字符为3个字节,这对东亚地区的语言合适。代理对为4个字节。(摘自于Windows核心编程)

缺点:


如果值在0x080以上进行字符编码不如UTF-16高效。

UTF-32编码

(由于时间问题,我对于字符编码尚未过于深入,此文以后再更新。。。发表于2019-7月27日)

你可能感兴趣的:(计算机组成原理)