计算机是如何存储的

计算机:二进制的世界

1110 0100 1011 1101 1010 0000 1110 0101 1010 0101 1011 1101
上面一行字就是“你好”在计算机中的表示方式

下面就来聊一聊计算机存储的过程

第一步:内存如何存储 0 和 1 ?

image
  • 存储
    图中模拟计算机存储 0 和 1 的过程,所示的是一个8*8的晶体管方阵,可以将每一个圆点当作看成是一个小电池,当需要存储一串二进制信息时,竖向存选中(1或0),横向输送电量,1就充电(显示红色),0就不充电(显示白色)。
  • 读取
    晶体管存电很少,耗电很快(ms),需要多次刷新,在耗尽电量之前进行充电(ns),要保证1还是1(电量大于50%就是1,电量小于50%就是0)。
    CPU的赫兹就是每秒刷新多少次
    通过计算机能够存储 0 和 1,程序员将世间万物的信息存进了计算机。

第二步:如何存储数字?

十进制转二进制

  • 正数存储
    37(10)=3 * 10 ^1 + 7* 10^0 = 32 + 4 + 1=1 * 2^5 + 1 * 2^2 + 1 * 2^0=100101(2)
  • 负数存储
    由于计算机只能存储0和1,负号不能存储,负数会以补码形式存储
    -37(10) == -100101(2)
  • 小数存储
    把十分之一为底的数变成以二分之一为底的数,由于计算机只能存储0和1,小数会以浮点数的形式存储
    0.75(10) = 0.5 + 0.25 = 1 * 1/2 + 1 * 1/4 = 0.11(2)
    为了方便书写,一般会将二进制数写为十六进制数

第三步:如何存储字符

将每个字符编号,外国人将每个字符进行了编号0~127共128个ASCII码值。
ASCII美国信息交换标准代码

image

如果你想储存 a,那么就储存 97(10) 对应的二进制
a -> 0110 0001(2) -> 61(16)

如果你想储存 字符1,那么就储存 49(10) 对应的二进制
1 -> 0011 0001(2) -> 31(16)

为方便书写将二进制转成十六进制

第四步:如何存储中文

我国制定了常用中文字符集GB 2312 中国国家标准字符集)

而后微软将一些生僻字日韩字符等加入后推出了
GBK字符集

第五步:如何存储所有字符

Unicode 字符集

将全球文字统一到一个表里面,包括中日韩文字、藏文、盲文、楔形文字、颜文字:-)、绘文字😂
第六步:如何将Unicode存到计算机里
Unicode 需要使用 32 位(4字节)来存储字符,他的存储方式如下

//低效率方式
a -> 0000 0000 0000 0000 0000 0000 0110 0001 = 0061
你 -> 0000 0000 0000 0000 0100 1111 0110 0000 = 4F60 

由于其表示简单的字符时也使用4个字节,浪费了很多资源,为提高效率,人们开始使用UTF-8,UTF-8是Unicode存到计算机的一种编码方式,它不是字符集,它可以用来表示Unicode标准中的任何字符。

//高效率方式 UTF-8
a -> 01100001
你 -> 11100100 10111101 10100000 
  • 像“a”这种较短的字符(小于七位的),我们可以直接在前面补零表示
  • 像“你”这种长的字符,由于计算机读取时无法弄清楚是存储了一个 由两个一字节组成的字符还是一个由两字节组成的字符,解决步骤(UTF-8):
    1. 我们从高位依次划分6个字符进行分组,直到分出的组不足6个。即:0100 111101 100000
    2. 分别在每一组前面补全读取这一串二进制信息的规则,即:1110XXXX 10XXXXXX 10XXXXXX
    3. 1110XXXX:111表示计算机需要读取3个字节,每个字节的开头都是10,10+XXXXXX、10+XXXXXX: 所以后两个分组的开头也补上10,说明是和第一个字节一起的,组成一个字符。只有X才是原二进制有效的数据

UTF-8

UTF-8 是一种编码方式,不是字符集,更高效的将Unicode存储到计算机中

现实问题

编码问题

JavaScript 使用了 Unicode 字符集,但没有使用 UTF-8 编码
JavaScript使用 UCS-2 编码,因为1995年 UTF-16还没有发明出来,JavaScript也不想使用 UTF-32

后果

ES5 无法表示 \uFFFF之后的字符(如 \u1D306),某些情况下会出bug
语言层面的问题,需要留心。

参考资料