C语言学习(四)ASCII、GB2312、GBK等编码以及Unicode等字符集

目录

  • (一)简介
  • (二)ASCII编码
    • 拉丁字母
    • ASCII编码介绍
    • ASCII码表
    • ASCII码和C语言
  • (三)GB2312编码和GBK编码,将中文存入计算机
    • 介绍
    • 如何存储
    • 中文编码方案
    • 牛掰的GBK编码
  • (四)Unicode字符集,将全世界的文字存储到计算机
    • 诞生及介绍
    • Unicode字符集是如何存储的
    • 对比以上三种编码方案
    • 宽字符和窄字符(多子节字符)

(一)简介

  之前我们说过,计算机是以二机制形式来存储的,他只认识0和1两个数字。我们在屏幕上看到的文字在存储前已经被转换成了二进制,在显示时也要根据二进制找到对应的字符。

  所以,特定的字符必然对应着固定的二进制,否则在转换时会造成混乱。那么,怎样将文字与二进制对应起来呢?这就需要一套规范,计算机公司和软件开发公司都要遵守,这样的一套规范就被称为字符集(Character Set)字符编码(Character Encoding)

严格来说,字符集和字符编码不是一个概念,字符集定义了文字与二进制的对应关系,为字符分配了唯一编号。而字符编码规定了如何将文字的编号存在计算机中。我们先暂时认为他们是同一个东西,后文会做详细介绍。

  字符集为每个字符分配一个唯一的编号,类似于学生的学号。通过这些编号就能找到对应的字符。

  可以将字符集理解为一个很大表格,里面列出了所有字符和二进制的对应关系。计算机显示文字或者存储文字,就是一个查表的过程。

  在计算机发展的过程中,先后出现了几十种甚至上百种的字符集,有些还在使用,有些已经淹没在历史的长河中。接下来会介绍几种现在还在使用的字符集以及了解一些现在未使用的一些字符集。

(二)ASCII编码

拉丁字母

  在正式介绍ASCII编码之前,我们先来说说什么是拉丁字母。估计有不少读者和我一样,对拉丁字母、英文字母和汉语拼音中的字母关系不是很清楚。

  拉丁字母也叫罗马字母,它源自于希腊字母。是当今世界上使用最广泛的字母系统。最基本的拉丁字母就是我们最常见到的ABCD等26个英文字母。

拉丁字母、阿拉伯字母、斯拉夫字母(西里尔字母)被称为世界三大字母体系。

  拉丁字母原先是欧洲使用的,后来由于欧洲殖民主义,导致这套体系在全球范围内流行开来。美洲、非洲、亚洲、澳洲都没逃过西方文化的影响。中国也是,我们现在用的汉语拼音其实就是拉丁字母,是不折不扣的舶来品。

  后来很多国家对拉丁字母进行了扩展,以适应本地语言文化。最常见的扩展方式就是加上变音符号,例如汉语拼音中的ü,就是在u的基础上加上两个小点演化而来;再如,áà就是在a的上面标上音调。

  总结:

  • 基本拉丁字母就是26个英文字母
  • 扩展拉丁字母就是在26个英文字母的基础上添加变音符号、横线、斜线等,每个国家不一样

ASCII编码介绍

  计算机是美国人发明的,他们首先要考虑的是如何将二进制与英文字母(也就是拉丁文)对应起来。

  当时各个厂家和公司都有自己的做法,编码规则并不统一。这给不同计算机之间的数据交互造成了很大麻烦。但是相对来说,能够得到普遍认可的有IBM发明的EBCDIC和这边要谈的ASCII。

  我们先说ASCII(American Standard Code for Information Interchange,美国信息交换标准代码),看名字就知道,这套编码是美国人给自己设计的。他们并没有考虑欧洲那些扩展拉丁字母,更没有日文、韩文,我大中华几万个汉字更不可能被重视。

  但这也无可厚非,美国人自己发明的计算机,当然要先解决自己的问题

  ASCII 的标准版本于 1967 年第一次发布,最后一次更新则是在 1986 年,迄今为止共收录了 128 个字符,包含了基本的拉丁字母(英文字母)、阿拉伯数字(也就是 1234567890)、标点符号(,.!等)、特殊符号(@#$%^&等)以及一些具有控制功能的字符(往往不会显示出来)。

  在 ASCII 编码中,大写字母、小写字母和阿拉伯数字都是连续分布的(见下表),这给程序设计带来了很大的方便。例如要判断一个字符是否是大写字母,就可以判断该字符的 ASCII 编码值是否在 65~90 的范围内。

  EBCDIC 编码正好相反,它的英文字母不是连续排列的,中间出现了多次断续,给编程带来了一些困难。现在连 IBM 自己也不使用 EBCDIC 了,转而使用更加优秀的 ASCII。

  ASCII 编码已经成了计算机的通用标准,没有人再使用 EBCDIC 编码了,它已经消失在历史的长河中了。

ASCII码表

  标准的ASCII码表共收录了128个字符,其中包含33个控制字符(具有某些特殊功能但是无法显示的字符)和95个可显示字符。

二进制 十进制 十六进制 字符/缩写 解释
00000000 0 00 NUL (NULL) 空字符
00000001 1 01 SOH (Start Of Headling) 标题开始
00000010 2 02 STX (Start Of Text)
00000011 3 03 ETX (End Of Text) 正文结束
00000100 4 04 EOT (End Of Transmission) 传输结束
00000101 5 05 ENQ (Enquiry) 请求
00000110 6 06 ACK (Acknowledge) 回应/响应/收到通知
00000111 7 07 BEL (Bell) 响铃
00001000 8 08 BS (Backspace) 退格
00001001 9 09 HT (Horizontal Tab) 水平制表符
00001010 10 0A LF/NL(Line Feed/New Line) 换行键
00001011 11 0B VT (Vertical Tab) 垂直制表符
00001100 12 0C FF/NP (Form Feed/New Page) 换页键
00001101 13 0D CR (Carriage Return) 回车键
00001110 14 0E SO (Shift Out) 不用切换
00001111 15 0F SI (Shift In) 启用切换
00010000 16 10 DLE (Data Link Escape) 数据链路转义
00010001 17 11 DC1/XON(Device Control 1/Transmission On) 设备控制1/传输开始
00010010 18 12 DC2 (Device Control 2) 设备控制2
00010011 19 13 DC3/XOFF(Device Control 3/Transmission Off) 设备控制3/传输中断
00010100 20 14 DC4 (Device Control 4) 设备控制4
00010101 21 15 NAK (Negative Acknowledge) 无响应/非正常响应/拒绝接收
00010110 22 16 SYN (Synchronous Idle) 同步空闲
00010111 23 17 ETB (End of Transmission Block) 传输块结束/块传输终止
00011000 24 18 CAN (Cancel) 取消
00011001 25 19 EM (End of Medium) 已到介质末端/介质存储已满/介质中断
00011010 26 1A SUB (Substitute) 替补/替换
00011011 27 1B ESC (Escape) 逃离/取消
00011100 28 1C FS (File Separator) 文件分割符
00011101 29 1D GS (Group Separator) 组分隔符/分组符
00011110 30 1E RS (Record Separator) 记录分离符
00011111 31 1F US (Unit Separator) 单元分隔符
00100000 32 20 (Space) 空格
00100001 33 21 !
00100010 34 22 "
00100011 35 23 #
00100100 36 24 $
00100101 37 25 %
00100110 38 26 &
00100111 39 27
00101000 40 28 (
00101001 41 29 )
00101010 42 2A *
00101011 43 2B +
00101100 44 2C ,
00101101 45 2D -
00101110 46 2E .
00101111 47 2F /
00110000 48 30 0
00110001 49 31 1
00110010 50 32 2
00110011 51 33 3
00110100 52 34 4
00110101 53 35 5
00110110 54 36 6
00110111 55 37 7
00111000 56 38 8
00111001 57 39 9
00111010 58 3A :
00111011 59 3B ;
00111100 60 3C <
00111101 61 3D =
00111110 62 3E >
00111111 63 3F ?
01000000 64 40 @
01000001 65 41 A
01000010 66 42 B
01000011 67 43 C
01000100 68 44 D
01000101 69 45 E
01000110 70 46 F
01000111 71 47 G
01001000 72 48 H
01001001 73 49 I
01001010 74 4A J
01001011 75 4B K
01001100 76 4C L
01001101 77 4D M
01001110 78 4E N
01001111 79 4F O
01010000 80 50 P
01010001 81 51 Q
01010010 82 52 R
01010011 83 53 S
01010100 84 54 T
01010101 85 55 U
01010110 86 56 V
01010111 87 57 W
01011000 88 58 X
01011001 89 59 Y
01011010 90 5A Z
01011011 91 5B [
01011100 92 5C \
01011101 93 5D ]
01011110 94 5E ^
01011111 95 5F _
01100000 96 60 `
01100001 97 61 a
01100010 98 62 b
01100011 99 63 c
01100100 100 64 d
01100101 101 65 e
01100110 102 66 f
01100111 103 67 g
01101000 104 68 h
01101001 105 69 i
01101010 106 6A j
01101011 107 6B k
01101100 108 6C l
01101101 109 6D m
01101110 110 6E n
01101111 111 6F o
01110000 112 70 p
01110001 113 71 q
01110010 114 72 r
01110011 115 73 s
01110100 116 74 t
01110101 117 75 u
01110110 118 76 v
01110111 119 77 w
01111000 120 78 x
01111001 121 79 y
01111010 122 7A z
01111011 123 7B {
01111100 124 7C
01111101 125 7D }
01111110 126 7E ~
01111111 127 7F DEL (Delete) 删除

  上表列出的是标准的 ASCII 编码,它共收录了 128 个字符,用一个字节中较低的 7 个比特位(Bit)足以表示(27 = 128),所以还会空闲下一个比特位,它就被浪费了。

ASCII码和C语言

  稍微有点C语言基础的会认为C语言用的就是ASCII编码,字符在存储时会转换成对应的ASCII码值,在读取时也是根据ASCII码找到对应字符,这句话时错误的。C语言有时候使用ASCII码有时候不是,他有时候会使用接下来我们要说的GBK 编码和 Unicode 字符集。我们将在后续内容中详细介绍。

(三)GB2312编码和GBK编码,将中文存入计算机

介绍

  计算机是一种轰动全国的发明,很快就从美国传到了世界各地,并得到广泛的认可,成了一种不可替代的工具。在计算机广泛流行的过程中遇到的一个棘手的问题就是字符编码。计算机是美国人发明的,他们用的是ASCII编码,只能显示英文字符,对汉语、韩语、日语、德语等其他国家的字符无能为力。

  为了让本国公民也使用上计算机,各个国家开始也开始效仿ASCII,开发属于他们自己的字符编码。这些编码和ASCII一样,只考虑本国的语言文化,不兼容其他国家的文字。这样做的后果就是一台计算机上必须安装多个字符编码,否则就不能正确的跨国传输数据。例如,在中国编写的文本文件,在日本的电脑上就无法打开,或者打开就是一堆乱码。

下面列出了常见的字符编码:

字符编码 说明
ISO/IEC 8859 欧洲字符集,支持丹麦语、荷兰语、德语、意大利语、拉丁语、挪威语、葡萄牙语、西班牙语,瑞典语等,1987 年首次发布。

ASCII 编码只包含了基本的拉丁字母,没有包含欧洲很多国家所用到的一些扩展的拉丁字母,比如一些重音字母,带音标的字母等,ISO/IEC 8859 主要是在 ASCII 的基础上增加了这些衍生的拉丁字母。
Shift_Jis 日语字符集,包含了全角及半角拉丁字母、平假名、片假名、符号及日语汉字,1978 年首次发布。
Big5 繁体中文字符集,1984 年发布,通行于台湾、香港等地区,收录了 13053 个中文字、408个普通字符以及 33 个控制字符。
GB2312 简体中文字符集,1980 年发布,共收录了 6763 个汉字,其中一级汉字 3755 个,二级汉字 3008 个;同时收录了包括拉丁字母、希腊字母、日文平假名及片假名字母、俄语西里尔字母在内的 682 个字符。
GBK 中文字符集,是在 GB2312 的基础上进行的扩展,1995 年发布。

GB2312 收录的汉字虽然覆盖了中国大陆 99.75% 的使用频率,满足了基本的输入输出要求,但是对于人名、古汉语等方面出现的罕用字(例如朱镕基的“镕”就没有被 GB2312 收录),GB2312 并不能处理,所以后来又对 GB2312 进行了一次扩展,形成了一种新的字符集,就是 GBK。

GBK 共收录了 21886 个汉字和图形符号,包括 GB2312 中的全部汉字、非汉字符号,以及 BIG5 中的全部繁体字,还有一些生僻字。
GB18030 中文字符集,是对 GBK 和 GB2312 的又一次扩展,2000 年发布。

  GB18030 |共收录 70244 个汉字,支持中国国内少数民族的文字,以及日语韩语中的汉字。

  由于ASCII先入为主,已经使用了十来年,现有很多软件和文档都是基于ASCII的,所以后来的这些字符编码都是在ASCII基础上进行扩展的,他们都兼容ASCII,以支持这些软件和文档。

  兼容ASCII的含义是,原来ASCII中已经包含的字符,在国家编码(地区编码)中的位置不变(也就是编码值不变),只是在这些字符的后面增添了新的字符。

如何存储

  标准 ASCII 编码共包含了 128 个字符,用一个字节就足以存储(实际上是用一个字节中较低的 7 位来存储),而日文、中文、韩文等包含的字符非常多,有成千上万个,一个字节肯定是不够的(一个字节最多存储 28 = 256 个字符),所以要进行扩展,用两个、三个甚至四个字节来表示。

  在制定字符编码时还要考虑内存利用率的问题。我们经常使用的字符,其编码值一般都比较小,例如字母和数字都是 ASCII 编码,其编码值不会超过 127,用一个字节存储足以,如果硬要用多个字节存储,就会浪费很多内存空间。

  为了达到「既能存储本国字符,又能节省内存」的目的,Shift-Jis、Big5、GB2312 等都采用变长的编码方式:

  • 对于原来的 ASCII 编码部分,用一个字节存储足以;
  • 对于本国的常用字符(例如汉字、标点符号等),一般用两个字节存储;
  • 对于偏远地区,或者极少使用的字符(例如藏文、蒙古文等),才使用三个甚至四个字节存储。

  总体来说,越常用的字符占用的内存越少,越罕见的字符占用的内存越多。

中文编码方案

  GB2312 --> GBK --> GB18030 是中文编码的三套方案,出现的时间从早到晚,收录的字符数目依次增加,并且向下兼容。GB2312 和 GBK 收录的字符数目较少,用 1~2个字节存储;GB18030 收录的字符最多,用1、2、4 个字节存储。

  1. 从整体上讲,GB2312 和 GBK 的编码方式一致,具体为:
  • 对于 ASCII 字符,使用一个字节存储,并且该字节的最高位是 0,这和 ASCII 编码是一致- 的,所以说 GB2312 完全兼容 ASCII。
  • 对于中国的字符,使用两个字节存储,并且规定每个字节的最高位都是 1。

  例如对于字母A,它在内存中存储为 01000001;对于汉字中,它在内存中存储为 11010110 11010000。由于单字节和双字节的最高位不一样,所以字符处理软件很容易区分一个字符到底用了几个字节。

  1. GB18030 为了容纳更多的字符,并且要区分两个字节和四个字节,所以修改了编码方案,具体为:
  • 对于 ASCII 字符,使用一个字节存储,并且该字节的最高位是 0,这和 ASCII、GB2312、GBK 编码是一致的。
  • 对于常用的中文字符,使用两个字节存储,并且规定第一个字节的最高位是 1,第二个字节的高位最多只能有一个连续的 0(第二个字节的最高位可以是 1 也可以是 0,但是当它是 0 时,次高位就不能是 0 了)。注意对比 GB2312 和 GBK,它们要求两个字节的最高位为都必须为 1。
  • 对于罕见的字符,使用四个字节存储,并且规定第一个和第三个字节的最高位是 1,第二个和第四个字节的高位必须有两个连续的 0。

  例如对于字母A,它在内存中存储为 01000001;对于汉字中,它在内存中存储为 11010110 11010000;对于藏文གྱུ,它在内存中的存储为 10000001 00110010 11101111 00110000。

  字符处理软件在处理文本时,从左往右依次扫描每个字节:

  • 如果遇到的字节的最高位是 0,那么就会断定该字符只占用了一个字节;
  • 如果遇到的字节的最高位是 1,那么该字符可能占用了两个字节,也可能占用了四个字节,不能妄下断论,所以还要继续往后扫描:
      如果第二个字节的高位有两个连续的 0,那么就会断定该字符占用了四个字节;
      如果第二个字节的高位没有连续的 0,那么就会断定该字符占用了两个字节。

  可见,当字符占用两个或者四个字节时,GB18030 编码要检测两次,处理效率比 GB2312 和 GBK 都低。

牛掰的GBK编码

  GBK 于 1995 年发布,这一年也是互联网爆发的元年,国人使用电脑越来越多,也许是 GBK 这头猪正好站在风口上,它就飞起来了,后来的中文版 Windows 都将 GBK 作为默认的中文编码方案。

  注意,这里我说 GBK 是默认的中文编码方案,并没有说 Windows 默认支持 GBK。Windows 在内核层面使用的是 Unicode 字符集(严格来说是 UTF-16 编码),但是它也给用户留出了选择的余地,如果用户不希望使用 Unicode,而是希望使用中文编码方案,那么这个时候 Windows 默认使用 GBK(当然,你可以选择使用 GB2312 或者 GB18030,不过一般没有这个必要)。

  接下来我们要讲解Unicode字符集和UTF-16编码方案。

  实际上,中文版 Windows 下的很多程序默认使用的就是 GBK 编码,例如用记事本程序创建一个 txt 文档、在 cmd 或者控制台程序(最常见的C语言程序)中显示汉字、用 Visual Studio 创建的源文件等,使用的都是 GBK 编码。

(四)Unicode字符集,将全世界的文字存储到计算机

诞生及介绍

  ASCII、GB2312、GBK、Shift_Jis、ISO/IEC 8859 等地区编码都是各个国家为了自己的语言文化开发的,不具有通用性,在一种编码下开发的软件或者编写的文档,拿到另一种编码下就会失效,必须提前使用程序转码,非常麻烦。

  人们迫切希望有一种编码能够统一世界各地的字符,计算机只要安装了这一种字编码,就能支持使用世界上所有的文字,再也不会出现乱码,再也不需要转码了,这对计算机的数据传递来说是多么的方便呀!

  就在这种呼吁下,Unicode 诞生了。Unicode 也称为统一码、万国码;看名字就知道,Unicode 希望统一所有国家的字符编码。

  Unicode 于 1994 年正式公布第一个版本,现在的规模可以容纳 100 多万个符号,是一个很大的集合。

有兴趣的读取可以转到 https://unicode-table.com/cn/ 查看 Unicode 包含的所有字符,以及各个国家的字符是如何分布的。

这个网站不太稳定,随时可能无法访问,不要问我为什么,访问不了也不要找我,没有比它更好的网站了。

  Windows、Linux、Mac OS 等常见操作系统都已经从底层(内核层面)开始支持 Unicode,大部分的网页和软件也使用 Unicode,Unicode 是大势所趋。

  不过由于历史原因,目前的计算机仍然安装了 ASCII 编码以及 GB2312、GBK、Big5、Shift-JIS 等地区编码,以支持不使用 Unicode 的软件或者文档。内核在处理字符时,一般会将地区编码先转换为 Unicode,再进行下一步处理。

Unicode字符集是如何存储的

  本节我们多次说 Unicode 是一套字符集,而不是一套字符编码,它们之间究竟有什么区别呢?

  严格来说,字符集和字符编码不是一个概念:

  • 字符集定义了字符和二进制的对应关系,为每个字符分配了唯一的编号。可以将字符集理解成一个很大的表格,它列出了所有字符和二进制的对应关系,计算机显示文字或者存储文字,就是一个查表的过程。
  • 而字符编码规定了如何将字符的编号存储到计算机中。如果使用了类似 GB2312 和 GBK 的变长存储方案(不同的字符占用的字节数不一样),那么为了区分一个字符到底使用了几个字节,就不能将字符的编号直接存储到计算机中,字符编号在存储之前必须要经过转换,在读取时还要再逆向转换一次,这套转换方案就叫做字符编码。

  有的字符集在制定时就考虑到了编码的问题,是和编码结合在一起的,例如 ASCII、GB2312、GBK、BIG5 等,所以无论称作字符集还是字符编码都无所谓,也不好区分两者的概念。而有的字符集只管制定字符的编号,至于怎么存储,那是字符编码的事情,Unicode 就是一个典型的例子,它只是定义了全球文字的唯一编号,我们还需要 UTF-8、UTF-16、UTF-32 这几种编码方案将 Unicode 存储到计算机中。

  Unicode 可以使用的编码方案有三种,分别是:

  • UTF-8:一种变长的编码方案,使用 1~6 个字节来存储
  • UTF-32:一种固定长度的编码方案,不管字符编号大小,始终使用 4 个字节来存储
  • UTF-16:介于 UTF-8 和 UTF-32 之间,使用 2 个或者 4 个字节来存储,长度既固定又可变

  UTF 是 Unicode Transformation Format 的缩写,意思是“Unicode转换格式”,后面的数字表明至少使用多少个比特位(Bit)来存储字符。

  1. UTF-8

  UTF-8 的编码规则很简单:

  • 如果只有一个字节,那么最高的比特位为 0,这样可以兼容 ASCII;
  • 如果有多个字节,那么第一个字节从最高位开始,连续有几个比特位的值为 1,就使用几个字节编码,剩下的字节均以 10 开头。

  具体的表现形式为:

  • 0xxxxxxx:单字节编码形式,这和 ASCII 编码完全一样,因此 UTF-8 是兼容 ASCII 的;
  • 110xxxxx 10xxxxxx:双字节编码形式(第一个字节有两个连续的 1)
  • 1110xxxx 10xxxxxx 10xxxxxx:三字节编码形式(第一个字节有三个连续的 1)
  • 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx:四字节编码形式(第一个字节有四个连续的 1)

  xxx 就用来存储 Unicode 中的字符编号。

  下面是一些字符的 UTF-8 编码实例(绿色部分表示本来的 Unicode 编号):

字符 字母N 符号æ 中文⻬
Unicode 编号(二进制) 01001110 11100110 00101110 11101100
Unicode 编号(十六进制) 4E E6 2E EC
UTF-8 编码(二进制) 01001110 11000011 10100110 11100010 10111011 10101100
UTF-8 编码(十六进制) 4E C3 A6 E2 BB AC

  对于常用的字符,它的 Unicode 编号范围是 0 ~ FFFF,用 1~3 个字节足以存储,只有及其罕见,或者只有少数地区使用的字符才需要 4~6个字节存储。

  1. UTF-32

  UTF-32 是固定长度的编码,始终占用 4 个字节,足以容纳所有的 Unicode 字符,所以直接存储 Unicode 编号即可,不需要任何编码转换。浪费了空间,提高了效率。

  1. UTF-16

  UFT-16 比较奇葩,它使用 2 个或者 4 个字节来存储。

  对于 Unicode 编号范围在 0 ~ FFFF 之间的字符,UTF-16 使用两个字节存储,并且直接存储 Unicode 编号,不用进行编码转换,这跟 UTF-32 非常类似。

  对于 Unicode 编号范围在 10000~10FFFF 之间的字符,UTF-16 使用四个字节存储,具体来说就是:将字符编号的所有比特位分成两部分,较高的一些比特位用一个值介于 D800~DBFF 之间的双字节存储,较低的一些比特位(剩下的比特位)用一个值介于 DC00~DFFF 之间的双字节存储。

  如果你不理解什么意思,请看下面的表格:

Unicode 编号范围(十六进制) 具体的 Unicode 编号(二进制) UTF-16 编码 编码后的字节数
0000 0000 ~ 0000 FFFF xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx 2
0001 0000—0010 FFFF yyyy yyyy yyxx xxxx xxxx 110110yy yyyyyyyy 110111xx xxxxxxxx 4

  位于 D800~0xDFFF 之间的 Unicode 编码是特别为四字节的 UTF-16 编码预留的,所以不应该在这个范围内指定任何字符。如果你真的去查看 Unicode 字符集,会发现这个区间内确实没有收录任何字符。

  UTF-16 要求在制定 Unicode 字符集时必须考虑到编码问题,所以真正的 Unicode 字符集也不是随意编排字符的。

对比以上三种编码方案

  首先,只有 UTF-8 兼容 ASCII,UTF-32 和 UTF-16 都不兼容 ASCII,因为它们没有单字节编码。

  1. UTF-8 使用尽量少的字节来存储一个字符,不但能够节省存储空间,而且在网络传输时也能节省流量,所以很多纯文本类型的文件(例如各种编程语言的源文件、各种日志文件和配置文件等)以及绝大多数的网页(例如百度、新浪、163等)都采用 UTF-8 编码。

  UTF-8 的缺点是效率低,不但在存储和读取时都要经过转换,而且在处理字符串时也非常麻烦。例如,要在一个 UTF-8 编码的字符串中找到第 10 个字符,就得从头开始一个一个地检索字符,这是一个很耗时的过程,因为 UTF-8 编码的字符串中每个字符占用的字节数不一样,如果不从头遍历每个字符,就不知道第 10 个字符位于第几个字节处,就无法定位。

  不过,随着算法的逐年精进,UTF-8 字符串的定位效率也越来越高了,往往不再是槽点了。

  1. UTF-32 是“以空间换效率”,正好弥补了 UTF-8 的缺点,UTF-32 的优势就是效率高:UTF-32 在存储和读取字符时不需要任何转换,在处理字符串时也能最快速地定位字符。例如,在一个 UTF-32 编码的字符串中查找第 10 个字符,很容易计算出它位于第 37 个字节处,直接获取就行,不用再逐个遍历字符了,没有比这更快的定位字符的方法了。

  但是,UTF-32 的缺点也很明显,就是太占用存储空间了,在网络传输时也会消耗很多流量。我们平常使用的字符编码值一般都比较小,用一两个字节存储足以,用四个字节简直是暴殄天物,甚至说是不能容忍的,所以 UTF-32 在应用上不如 UTF-8 和 UTF-16 广泛。

  1. UTF-16 可以看做是 UTF-8 和 UTF-32 的折中方案,它平衡了存储空间和处理效率的矛盾。对于常用的字符,用两个字节存储足以,这个时候 UTF-16 是不需要转换的,直接存储字符的编码值即可。

  Windows 内核、.NET Framework、Cocoa、Java String 内部采用的都是 UTF-16 编码。UTF-16 是幕后的功臣,我们在编辑源代码和文档时都是站在前台,所以一般感受不到,其实很多文本在后台处理时都已经转换成了 UTF-16 编码。

  不过,UNIX 家族的操作系统(Linux、Mac OS、iOS 等)内核都采用 UTF-8 编码,我们就不去争论谁好谁坏了。

宽字符和窄字符(多子节字符)

  有的编码方式采用 1~n 个字节存储,是变长的,例如 UTF-8、GB2312、GBK 等;如果一个字符使用了这种编码方式,我们就将它称为多字节字符,或者窄字符。

  有的编码方式是固定长度的,不管字符编号大小,始终采用 n 个字节存储,例如 UTF-32、UTF-16 等;如果一个字符使用了这种编码方式,我们就将它称为宽字符。

  Unicode 字符集可以使用窄字符的方式存储,也可以使用宽字符的方式存储;GB2312、GBK、Shift-JIS 等国家编码一般都使用窄字符的方式存储;ASCII 只有一个字节,无所谓窄字符和宽字符。

  说了这么多,C语言到底使用哪种字编码方式呢?其实这个问题有点复杂,我们将在后文展开讲解。

你可能感兴趣的:(C语言基础,C语言,c语言)