概述

我们看下字符和字符串的表示,大体分为如下三种:

  • 英文字符
  • 中文字符
  • 字符串

一、英文字符

  • 英文在计算机里就是用ASCII码来表示的,我们平时使用的这些键盘有一些ABCD这样的字母,还有012345这样的一些数字
  • 当然还有一些我们在屏幕上没办法打印出来的,用于控制计算机的一些按键,一些字符,比如说上,下,delete删除,之类的一些字符
  • 由于计算机硬件只能识别0101这样的二进制数,所以我们必须制定一个规则,把这些字符把它映射为0101的二进制数
  • 常用的英文字符和控制字符这些总共只有128个,所以我们只需要7个二进制位进行编码就可以
  • 因为2的7次方等于128,所以7个二进制位足够表示128个字符,能够进行一一映射
  • 只不过由于计算机处理数据的时候,通常都是以一个字节为单位,也就是8个比特为单位,所以虽然理论上我们只需要7位就可以表示128个字符
  • 但是实际存到计算机里的时候,通常会在高位补一个0,也就是凑足一个字节
  • 上图这张表有128个ASCII编码的字符,这些字符的编码排列是有一定规律的
  • 首先32~126这个区间内的这些字符,比如53号字符对应的是阿拉伯数字5,还有68号对应的是大写的D这个英文字符
  • 而32号对应的SPC指的是space,是空格这个字符
  • 总之, 32~126这些字符都是我们平时可以在计算机屏幕上看到的可以打印出来的字符, 称为可印刷字符
  • 而其余的这些字符就是用于控制和通信的,什么叫控制和通信呢?
    • 比如127号字符,这个其实是delete的一个缩写,顾名思义,当我们在键盘上按下delete这个按键之后,计算机就会执行相应的比如删除字符之类的工作
    • 因此,这样的字符是用于控制计算机来执行某一种行为的,所以这类的字符叫做控制字符
    • 然后再举一个通信字符的一个例子,比如6号字符ACK, 在计算机网络中,当两台计算机进行网络通信的时候,其中一台计算机收到另一台计算机发来的报文
    • 那么需要给它回复一个ACK这样的一个信号,所以,ACK这个字符就是一个很典型的, 用于实现计算机之间通信的字符
    • 总之0~31还有127,这几个字符都是用于控制或者用于通信的
  • 接下来我们再把目光放到可印刷字符这个部分来看一下有什么规律
    • 48 ~ 57这个区间内刚好是我们阿拉伯数字0~9这个范围, 那48, 49, 50这些只是十进制表示形式,如果把它翻译成二进制的话
    • 48是0011 0000; 57是0011 1001,也就是说所有这些数字字符的前4个比特位都是一样的
    • 而后4个比特位,它刚好对应了这些字符的bcd码,或者更确切的说应该是8421码
    • 9这个字符的后4个比特位是1001,那么如果用8421码的规则来解释的话,不刚好就是9么
    • 以上是这些数字字符的规律
  • 另外我们再来看英文字母,大写字母的区间是这样的一个范围,65 ~ 90,然后小写字母是97 ~ 122
  • 所有的这些大写字母,还有所有的这些小写字母,这些相邻的字母,他们的ASKII码也是相邻的
    • 大写字母:65 (0100 0001) ~ 90 (0101 1010)
    • 小写字母:97 (0110 0001) ~ 122 (0111 1010)
  • 所有的大写字母的前三个比特都是010,而后面的5个比特币转换成10进制的话刚好是 1 ~ 26,它们对应了26个英文字母
  • 所以,这26个英文字母的后5个比特位的编号刚好就分别对应1~26号
  • 那小写字母也是一样的,前边的三个比特位都是011,而后边的5个比特位分别就是 1 ~ 26 这样的一个范围

例题

  • 已知字母大写的A它的ASCII码值是65,字符H存放在存储单元M中,问存储单元m当中存放的内容是什么?
    • 1 ) 思路1:
    • 显然这里面存放的就是H这个字母所对应的ASCII码
    • 这个题是用A的ASCII码推出H的ASCII码,A是第一个字母,而H是第8个字母
    • 所以H的码值应该是在A的基础上再加7,于是65 + 7 = 72,我们再把这个十进制翻译成二进制得到: 100 1000
    • 由于每个字母的ASCII码都是8个比特位,所以我们需要补一个0凑足8位,那这就得出了这道题的答案: 0100 1000
    • 2 ) 思路2:
    • 所有的这些大写字母的前三位都是一样的,65的前三位是010只有后五位不一样,那么A是第1个字母,所以它的后5位二进制刚好就是0 0001, 也就是1
    • 而H是第8个字母,所以H的后5个二进制位翻译成10进制,刚好应该对应8,所以我们只需要把后面的5个二进制位表示成8 就可以了, 也就是:0 1000
    • 所以最终M中存放的内容为:0100 1000

总结

  • 所有的大写字母的asc码值都是连续的,所有的小写字母的ASCII码值也是连续的
  • 只要抓住这个规律就很简单了,由于英文里面只有26个字母的大小写,再算上标点符号各种控制符号
  • 128个字符就足够英文世界的使用了,所以在计算机里表示英文只需要8个比特的信息就足够,甚至用7个比特就可以

二、中文字符

汉字的表示和编码

  • 对于中文来说我们的汉字很多,常用的汉字就有3500多个,再加上各种各样的生僻字,汉字里边有可能出现的字符是几万个
  • 要表示几万个汉字的字符,显然8个比特的信息是不够的。8个比特的二进制最多对应256种状态,也就是只能对256个字符
  • 为了解决汉字在计算机内部的表示,1980年我们国家推出了一个汉字编码的标准叫做GB2312
  • 在80年版的这个汉字编码当中,我们的汉字再加各种各样的什么标点符号之类的东西,总共只有7000多个
  • 也就是并没有全部囊括几万个汉字,我们来看下这些汉字编码是如何设计的
  • 这些汉字总共被分为了94个区,每个区有94个位置,直观一点来讲,其实就是定义了一个94行94列的格子
  • 给这94个区分别进行编号,再给每个区的94个位置分别进行编号,我们就可以用区号和位置号或者说横坐标和纵坐标来对应某一个汉字了
  • 每一个汉字所对应的区号和位置号,就是这个汉字的区位码,用这种区位码映射到汉字,其实是很直观的,很符合人类的理解,举个例子
  • 比如:”啊”这个字, 它的区码是16,位码是01,转化成16进制后分别对应:10H, 01H
  • 再回到之前的ASCII码
  • 现在假设A这台计算机给B这台计算机发送一系列的中文字符,如果用区位码这种编码方式A给B发送的话
  • 由于区码和位码都是0~93这个范围,如果A给B发送的第1个字节的信息刚好是6,当B收到的第1个字节是6
  • B计算机有可能会以为这是A给它发送了一个ACK,一个确认,除了ACK这个通信字符, 也会收到其他的一些通信或者控制字符,这时候也可能出现其他的一些错误
  • 所以如果区位码的第1个字节和第2个字节都处于0~93这个范围,那么有可能在进行数据传输,网络通信的时候,会出现一些意想不到的错误, 那怎么解决这个问题呢?
  • 其实很简单,我们用于控制和通信的字符是0~31这个范围, 如果我们把区位码的第1个字节和第2个字节都分别加上32,也就是加上20H(这个是32的16进制表示)
  • 都分别加上20H的话,就意味着区码和位码都会从32开始,区位码都大于32,在进行通信的时候,就不可能会出现刚才所说的这种异常的情况
  • 在区位码的基础上,第1个字节和第2个字节分别加上20H,进行了这样的处理之后就得到了所谓的国标码,也就是GB2312-80所规定的这个编码
  • 国标码的这种编码方式就可以满足数据的传输这种需求,但是如果要把汉字存到计算机里面,那还需要在国标码的基础上再分别加上8080H, 从而得到汉字内码, 全称是汉字机内码
  • 解释下这种奇怪的规定:区位码的区码和位码分别是0~93这样的一个范围,我们加上2020H之后,得到了国标码,国标码的两个字节就有可能是32 ~ 32+93 = 125
  • 有可能会落在这样的一个范围,32~125 刚好又是这些英文字符的一个表示范围,所以如果我们想让我们的中文编码和ASCII码能够兼容,不会相互冲突的话,我们就必须避开这个区间
  • 那怎么避免这个冲突呢,很简单,我们只需要在国标码的基础上,第1个字节和第2个字节都通通加上128,那经过这样的处理汉字的编码和英文的编码就不会再发生冲突
  • 一个汉字占两个字节,但是汉字的任何一个字节肯定都是大于128的,所以如果计算机读出一个字节的数据,发现这个字节落在了小于128的范围,那么就说明这个字节就是对应了某一个英文字符
  • 而如果这个字节它的范围刚好是在128之外。计算机就可以知道,除了这个字节之外,我们还需要读出下一个字节,然后把这两个字节组合起来把它对应上一个汉字
  • 在国标码基础上加上8080,是为了汉字编码和ASCII码能够相互兼容,因为十六进制的80H对应1000 0000B的二进制, 这个二进制数最高位是1,而ASCII编码的最高位都是0
  • 所以如果我们把两个字节的最高位都变成1的话,就可以和ASCII码进行一个区分,最终在计算机内部对汉字进行处理,其实就是使用这种汉字内码的编码方式
  • 这儿我们只是给出了汉字的其中一种编码方式,其实还有很多的编码,比如UTF8

汉字的输入编码

  • 输入编码就是用来输入汉字的,比如拼音,比如”内”这个汉字的拼音输入, 对应 “nei” + 某个数字
  • 输入软件把它转化成对应的国标码,再由系统或应用软件,把国标码转换成与之对应的汉字内码, 最终存储在我们的手机或者电脑里面
  • 这就是所谓的输入编码,输入编码要符合我们人类理解的一个习惯,所以这就是用来把汉字输入到计算机里面的方法
  • 那汉字要输出计算机,需要用到另一种编码叫做汉字的字形码

汉字的字形码

  • 比如”你”这个中文汉字,它其实就是由一个一个的这种像素点来组成的,那”你“这个汉字所对应的字形码
  • 如果用二进制的方式来看的话,就是这样的一个形式,二进制为1的地方表示这个像素点需要显示,而二进制为0的地方表示这个像素点不需要显示
  • 所以汉字的字形码就是把汉字输出计算机的时候使用的,需要把汉字内码转换成汉字的字形码
  • 也有可能是内码先转换成国标码,国标码再转换成字形码

字符串

字符串的存储

1 ) 场景1

  • 假设某计算机按字节编址(按字节编址的意思就是说每个内存地址会对应一个字节的内容)
  • 假设我们要存储abc这样的一个字符串, 从地址为2的单元开始,这几个字符所对应的ASCII编码分别是这样的
    • a: 0110 0001 = 61H
    • b: 0110 0010 = 62H
    • c: 0110 0011 = 63H
  • 2这个单元它存储的二进制信息是61H,然后3是62H,4是63H,也就是说一个字符串内的各个字符,从低地址到高地址依次被存放
  • 在很多语言当中为了标识一个字符串的结束,一般会在字符串的末尾再加上一个字节的信息,比如C语言的 \0 这个字符
    • \0: 0000 0000 = 00H
    • 这个字符的ASCII编码就是 00H
  • 所以在5这个地址上,还需要存放一个 \0 的信息,那这就是abc这个字符串在计算机里面的一个存储

2 ) 场景2

  • 再看下带有中文字符的字符串如何进行存储, 比如”abc啊”,前提和场景1一致,从2号存储单元开始
    • a: 0110 0001 = 61H
    • b: 0110 0010 = 62H
    • c: 0110 0011 = 63H
    • 啊: 机内码 = B0 A1H
  • B0是高位字节, A1是低位字节, “啊” 这个中文需要占两个字节
  • 也就是5号存储单元信息是:B0H, 6号存储单元信息是: A1H, 7号存储单元是附加字节信息: 00H, 这种方式称为 大端模式
  • 所谓大端模式: 是指将数据的最高有效字节存放在低地址单元中
  • 当然除了这种方式之外,其实还可以有另外一种存储方式,其实区别就在于”啊”这个中文的高位字节和低位字节存储顺序不一样
  • 我们称之为 小端模式:将数据的最高有效字节存放在高地址单元中
  • 这两种存储方式都是可以的
  • 注意:在所有计算机中,多字节数据都被存放在连续的字节序列中,根据数据中各字节的排列顺序不同,可能有”大端模式”、“小端模式”

总结

  • 字符与字符串
    • ASCII码
      • 通常用8bit表示一个字符,最高位都是0
      • 共128个字符。0 ~ 31、127为控制/通信字符;32 ~ 126 为可印刷字符
      • 所有大写字母、所有小写字母、所有数字的编码都连续
    • 汉字
      • 区位码(94*94)、国标码、汉字内码、输入编码、字形码
      • 国标码 = 区位码 + 2020H (源于区位码在通信时产生的问题)
      • 机内码 = 国标码 + 8080H (为了实现汉字编码与ASCII码的兼容)
    • 字符串
      • 从低地址到高地址逐个字符存储,常采用 \0 作为结尾标志
      • 对于多字节的数据(如汉字), 可采取大/小端存储模式
      • 大端模式: 将数据的最高有效字节存放在低地址单元中
      • 小端模式: 将数据的最高有效字节存放在高地址单元中