本篇主要讲解的是关于字符如何在计算机中存储,它所对应的数字映射是什么。关于存储简单讲下,计算机底层是基于电子元件的电学特性构建的,人们通过寄存器将电压保存下来,用电压的高低,分别映射数字中的1和0,计算机中所说的一位,只是一种基于逻辑上的最小差异假设,用数字0和1表示这种基本差异,然后根据特定的物理硬件实现了电压和逻辑上数字的映射。所以,当我们讨论存储时,讨论的也只是一种逻辑表示,具体如何用硬件实现,要参考硬件方面的相关知识。
假设是这样的:一位可以存储一个0和1,可表示两个差异,那么2位根据前后的排列,就可以表示4个差异,00,01,10,11,3位就是8个...以此类推,n位就可以存储2^n方个差异。换算成10进制可表示1/(log2-10)*n位的十进制整数,n/3位的八进制整数,n/4位的十六进制整数。这里的位也可直接看成二进制整数的位。
1字节(B)=8位(bit) 1千字节(Kb)=1024字节(B) 1兆(M)=1024千字节(Kb) 1吉(G)=1024兆(M)

一.ASCII码
ascii.png
百度百科上的,主要记住NULL是0,空格是32,字符0是48,大写字母A是65,小写字母a是97,算法可能会用到,剩下的查表就行。通过强制转换可查字符对应数字。ASCII码对于常用的128个字符进行了编码,实际只需7位就可存储,但是出于操作系统和硬件和设计,使其每个字符要占8位,最高位始终为0。
{

class Program
{
    static void Main(string[] args)
    {
        byte a = 73;
        char c = (char)a;
        Console.WriteLine(c);//输出:I
        Console.ReadLine();
    }
}

}

二.ISO8859-1
在ASCII码的基础上发展而来,将废弃的首位也进行了编排,可用字符达到了256个,依旧只占用1个字节。
iso.png
注:这张图片每个图标由上而下从0开始的序数就是二进制后的高四位,从左到右的序数是低四位。

三.GB2312
GB2312主要针对中文简体汉字而创造出的编码方式,但同时也兼容了ASCII码,因为汉字数目远远大于256,所以GB2312采用两个字节来存储。GB2312将要存储的字符集分为94个区,每个区至多有94个字符。
1~9区存放特殊符号,16~55区存放一级汉字,56~87区存放二级汉字,10~15区以及88~94区并未使用。
GB2312实际上只是字符集对于区号和尾号的映射,然而对于实际存储方法,通常采用EUC存储方法,“高位字节”使用了0xA1-0xF7(把01-87区的区号加上0xA0),“低位字节”使用了0xA1-0xFE(把01-94加上 0xA0)。 由于一级汉字从16区起始,汉字区的“高位字节”的范围是0xB0-0xF7,“低位字节”的范围是0xA1-0xFE,占用的码位是 72*94=6768。其中有5个空位是D7FA-D7FE。
下面节选了部分特殊符号。
GB2312.png

四.GBK
随着越来越多中文汉字出现,包括一些繁体,方言,生僻字,原来的GB2312编码方式已经不能满足需求,于是GBK诞生了,GBK即GB2312的扩展,因此,兼容了GB2312的字符映射。
GBK自然也兼容了ASCII码的映射,当字符序号小于127时,GBK将会用一个字节存储,当超过127时,GBK将会用两个字节存储。GBK新添了许多截取自其它各出处的字符,下面列一张百科上的图,具体内容可查百科。
GBK.png

五.Unicode码
因为各国间的编码并不统一,如我国使用GBK,英文语言则使用ASCII码,日语则使用JIS码,各国间的数据存储编码方式不同,传递数据时也更加繁琐和麻烦,为了解决这种编码不统一的问题,Unicode诞生了。Unicode码的字符集包含了全部国家的绝大多数字符,使全世界的数据都可以有一种编码方式是共通的。
Unicode共包含从0x000000~0x10FFFF总计1114112个符号映射位置,但实际使用并未能占用全部映射位。最高两位0x00~0x10共计17个存储方式又被称为17个平面,以00开头的第一个平面存储了常用的主要字符,称作BMP(基本多语言平面),其中,0xD800~0xDFFF之间的位置并未使用,同时,Unicode码前128位也兼容了ASCII码。
Unicode码也只是一种符号与数字的映射,具体编码有以下几种方式:

1.UCS-2
固定占两个字节,对应BMP上的字符,0xD800~0xDFFF同样未被使用。

2.UCS-4
固定占四个字节,但实际编码位置只有31位,可录入字符达到20多亿个,但实际使用字符未超过0X10FFFF。

3.UTF-8
utf-8编码对于不同长度的Unicode码占用不同的字节数为其存储,具体存储方式见下表
utf-8.png
ASCII码采用1字节,11位以内采用2字节,16位以内采用3字节,其余用4字节,多出来的位用固定的格式补位。

4.UTF-16
常用字符同UCS-2,65536以内的字符用两个字节
如果字符序数大于65536,用四个字节存储。先将0x10000~0x10FFFF的字符序数减去65536(0x10000),那么一定会得到0x00000~0xFFFFF范围的数,可用20位表示,前10位0xD800,后10位加上0xDC00,作为4字节的前两个字节和后两个字节
也即U-65536=U'=yyyyyyyyyyxxxxxxxxxx 编码:110110yyyyyyyyyy 110111xxxxxxxxxx

5.UTF-32
与UCS-4相同,但是限定使用范围到0x10FFFF,是UCS-4的子集。

大端序和小段序
大小端序通常只和处理器硬件有关,而和编码方式无关,如Intelx86就是小端序的。
小端序(LE)就是指多个字节的一个字符,高位字节在高位地址处,低位字节在高地址处。如:a->0x0061 存储为61 00
大端序(BE)就是00 61。

BOM
字节流标志,其“零宽无中段空格”会在一些utf-8,utf-16,utf-32编码的文件开头作为标志传输过去。
bom.png

这段程序用来判断C#程序输出的是什么编码,可以看出这里输出的值就是Unicode码的码点。
{

class Program
{
    static void Main(string[] args)
    {
        string s = "你好,世界";
        printStr(s);
        printEncodeStr(Encoding.Unicode, s);
        printEncodeStr(Encoding.UTF8, s);
        printEncodeStr(Encoding.Default, s);//大概是GBK
        printEncodeStr(Encoding.ASCII, s);
        Console.ReadKey();
    }
    static void printStr(string s)
    {
        foreach(char c in s)
        {
            ushort n = (ushort)c;
            Console.Write(Convert.ToString(n,16)+" ");
        }
        Console.WriteLine();
    }
    static void printEncodeStr(Encoding en,string s)
    {
        byte[] bytes = en.GetBytes(s);
        foreach(byte b in bytes)
        {
            Console.Write(Convert.ToString(b,16)+" ");
        }
        Console.WriteLine();
    }
}

}
output.png