彻底搞懂字符编码ASCII,GB2312,UNICODE,UTF-8


阅读了一篇关于编码的博客( 点击打开链接)后,自己做了下总结,解释一下基础知识

基础

能看到这篇文章, 我就假设你知道二进制,字节(byte),比特位(bit)这些概念了,如果不知道就先去了解下吧。

什么是字符编码?

我们都知道,计算机只能识别二进制,任何数据都是以二进制形式存储在计算机上的,拿现实生活中的数字为例,现实中的数字是十进制的,例如

0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10

在计算机上要保存为二进制形式,我们假设计算机用一个字节(也就是8个bit(一个bit表示一个二进制位))表示十进制数字,那么对应保存在计算机中的二进制就是(实际不是这么保存的,这里忽略了补码,反码,负数这些概念,因为这不是我们要讨论的重点):

0–>00000000

1–>00000001

2–>00000010

3–>00000011

4–>00000100

5–>00000101

6–>00000110

7–>00000111

8–>00001000

9–>00001001

10–>00001010

可以想到,我们用一个字节表示数字的话,一个字节共有2的8次方==256个状态,只表示正整数的话,我们可以用一个字节表示 0-255这256个正整数,这些字节是顺序存储在计算机内存中的,例如计算机保存了这么一段数据:

000010000000010100000111

计算机在识别的时候,会依次读取一个字节,例如第一个字节是00001000,表示8,同理再读取下一个字节,那么上面这段数据就认为这是857。好了,我们好像大概理解了数字是如何保存在计算机中并被计算机识别的了。

现实中除了123456这些数字,还有abcdef这些字符(以及中文字符,其他各国字符)需要计算机识别和表示,原理其实是差不多的。(字符的概念:简单通俗理解,字符为各国语言的最小单位,英语里的“a”是一个字符,中文里的"你"也是一个字符)

下面来理解一下字符编码,就是字符如何保存在计算计中并被计算机识别,和数字类似,假设我们用一个字节表示字符,例如,

我们用00000000表示a,用00000001表示b等等,计算机会一个字节一个字节的读取,读到00000000就知道这是a了,同理,一个字节有256种状态,那么一个字节就可以表示256个字符,这种把字符和相应的二进制一一对应起来,从而让计算机可以识别字符的过程,就是字符编码(实际计算机中肯定不会00000000对应a,00000001对应b这么简单的,这也是我们接下来要讨论的),可以想到,一个字节这肯定是不够的,我们有多少中文啊,其他国家还有多少字符呢。

正文

ASCII

就像基础中介绍的,ASCII是用7位bit表示字符的,所以只可以表示128个字符,一开始美国人觉得128个字符够用了。

ASCII扩展码

后来美国人发现128个字符不够用,就用8个bit(也就是一个字节)表示一个字符,那么可以表示256个字符了。

其中前128个和ASCII一模一样,后面增加128个新的字符而已,所以才叫ASCII扩展码,主要表示英文字符和一些打印字符,非打印字符等。

中国人说话了,一个字节最多256种状态,汉字远远不够表示啊。

GB2312

是对 ASCII 的中文扩展(注意是在ASCII码的基础上进行中文扩展,而不是在ASCII扩展码基础上)。也是直接存储方式。

规定把那些127号之后的奇异符号们直接取消掉(也就是取消扩展的ASCII码), 规定:一个小于127的字符的意义与原来相同,但两个大于127的字符连在一起时,就表示一个汉字,前面的一个字节(他称之为高字节)从0xA1用到 0xF7,后面一个字节(低字节)从0xA1到0xFE,这样我们就可以组合出大约7000多个简体汉字了。在这些编码里,我们还把数学符号、罗马希腊的字母、日文的假名们都编进去了,连在 ASCII 里本来就有的数字、标点、字母都统统重新编了两个字节长的编码,这就是常说的”全角”字符,而原来在127号以下的那些就叫”半角”字符了。 中国人民看到这样很不错,于是就把这种汉字方案叫做 “GB2312“。

GBK

把 GB2312 没有用到的码位找出来老实不客气地用上。 后来还是不够用,于是干脆不再要求低字节一定是127号之后的内码,只要第一个字节是大于127就固定表示这是一个汉字的开始,不管后面跟的是不是扩展字符集里的内容。结果扩展之后的编码方案被称为 GBK 标准。

DBCS

中国的程序员们看到这一系列汉字编码的标准是好的,于是通称他们叫做 “DBCS“(Double Byte Charecter Set 双字节字符集)。在DBCS系列标准里,最大的特点是两字节长的汉字字符和一字节长的英文字符并存于同一套编码方案里
举个例子解释两字节长的汉字字符和一字节长的英文字符并存于同一套编码方案里:

为了便于理解,我们用十进制解释,我们把a,b,c,d,e,f这6个英文字符,和“汉“,字”这两个字符用十进制编码,英文用一位十进制表示,中文用2位十进制表示:规定用单个的0-4表示英文a-e,大于4的数字,和下一位一起表示一个汉字,例如50表示“汉”,51表示“字”。那么1513350则表示“b字dd汉”,这就相当于把两位的汉字,和一位的英文共存于同一套编码方案。

ASCII系列编码是直接存储的,也就是说规定哪种字符对应哪种二进制,就把这种二进制原样存储在内存中。例如,假设编码规定00000000表示a,那么a保存在内存中就是00000000。后面会介绍UNICODE几种非直接存储(编码存储(UTF系列))

看到这,你也应该理解了,在ascii编码系列中程序员们常常念叨的一个英文字符是一个字节,一个中文字符是两个字节的含义了。

ascii编码系列最多用两个字节,最多也就表示2的16次方==65535个字符,加上其他国家的字符,还不够,怎么办?

UNICODE

Unicode也是一种字符编码方法, 不过它是由国际组织设计, 可以容纳全世界所有语言文字的编码方案。相当于为世界上每种字符都定义了一个数字来表示。
但是UNICODE虽然给每个字符都定义了一个数字表示,但是直接存储的话可能需要大于1个字节(例如假设字符a用256来表示,则存储在内存中为11111111,它是8位bit,占用了一个字节;假设字符b用257表示,则只能用两个字节表示000000100000000,同理,假如字符x用389表示呢,所以直接存储其二进制可能会造成长短不一),要将它按照字节存储,就有两个问题:

  1. 如何区分单独1个字节表示一个字符还是2个字节表示一个字符还是3个字节(注:ascii系列编码中是通过第一个字节是不是大于127来区分的)

  2. 那么为了方便区分, 如果unicode统一规定, 每个符号用三个或四个字节表示, 那么每个英文字母前都必然有二到三个字节是0, 这对于存储来说是极大的浪费,因为英文字母只用一个字节表示就够了(例如统一规定所有的字符都用三个字节表示那么字符a可能表示为000000000000000000000001,显然相比ansii编码多出了前两个字节的0很浪费),文本文件的大小会因此大出二三倍, 这是无法接受的

为了解决这两个问题,出现了UNICODE的不同编码方式,这些编码方式相当于规定了UNICODE码的存储规则,来解决上面两个问题,也就是说想办法既能很好的区分出不同的二进制表示的含义,又不那么浪费存储空间,所以需要存储UNICODE前,经过一些转换再存储,这就是UTF(Unicode Transformation Format:unicode转换格式)系列编码。

UTF-8

通过前导字节的方式(给每个字节加10标志位)来存储,既能很好的区分出不同的二进制表示的含义,又节省存储空间。是一种针对Unicode的可变长度字符编码,UTF-8用1到6个字节编码Unicode字符(所以对于UTF-8,一个字符是几个字节就不确定了,有可能是1个,2个或者3个甚至是6个)

注:关于utf-8的具体如何编码的,可以自行查看相关文档。

UTF-16(USC-2)

定死两个字节表示一个字符(显然对于英文字符会有一个字节的浪费),通常说的UNICODE就是指的UTF-16,以小端方式存储。(对于UTF-16,无论是中文还是英文,一个字符都是两个字节)

UTF-32(USC-4)

定死四个字节表示一个字符,相比UTF-16是范围更广了,表示的字符更多了,同时空间浪费也更加严重

编程语言对字符编码的支持

总结一下上面的字符编码:

  • ASCII编码的字符只需要一个字节就能存储

  • GB2312,GBK等DBCS系列需要一个或两个字节存储

  • UTF-8需要1-6个字节存储

  • UTF-16需要2个字节存储,windows中的UNICODE编码就是用UTF-16存储的,所以在VC编程中,一说UNICODE就是指UTF-16

这些需要不同存储空间的字符编码,该如何保存在程序中呢,毕竟c语言一开始只有char类型(一个字节)来表示字符,聪明的人类总是有办法,把字符编码分类:

  • ASCII,DBCS系列,utf-8等需要一个或多个存储空间的编码表示的字符串叫多字节编码字符串,用char数组保存。

  • UTF-16,也就是UNICODE,需要固定2个字节空间的编码表示的字符串叫宽字节编码字符串,用w_char数组保存,一个w_char两个字节(平台相关,在win32下w_char两个字节,在linux下wi_char是四个字节)。

有了多字节和宽字节的分类后,所有的字符串相关的操作,都有两套API分别针对多字节和宽字节,例如计算字符串长度的函数,对于多字节字符串用strlen计算,对于宽字节字符串用wcslen计算。

猜你喜欢

转载自blog.csdn.net/rankun1/article/details/100140477
今日推荐