字符集与字符编码
字符集
计算机存储信息都是通过二进制的来进行存储,试想:计算机如何是显示图像的?计算机显示图像的时候是通过一个一个的像素点来绘制出来的。计算机是怎么知道如何绘制每一个字体的呢。答案就是:计算机通过字体的像素矩阵来对应绘制出的字体。那么如果在存储文件的时候储存的是一个像素矩阵的机器码表示 ,比如说存储a的像素矩阵的机器码表示,那么一个a字母所占用的字节就非常的多,储存的内容很冗余,非常的浪费了储存空间。就就像利用编程来解决问题一样,可以定义了一个集合,该集合存储的时候,key为唯一的字符编码,value则为对应字体的字形等信息,那么储存a字母的时候就储存a所对应集合中的地址,两个字节即可以表示65535个地址,三个字节即可表示16777216个地址,如果采用3个字节的key这么大的集合,那么足以装下世界上所有的字体了。
常用的字符集有:ASCII字符集、GB2312字符集、BIG5字符集、GB18030字符集、Unicode字符集等
字符的编码
以记事本为例Windows的记事本户提供如下4种编码方式,默认的是ANSI也就是系统遗留的编码,在中文操作系统中就是GBK编码。
新建两个文件:
格式为系统默认的格式GBK
这里我们可以用代码来查看保存的文件的字节码
import java.io.*;
import java.util.Arrays;
public class Main {
public static void main(String[] args) throws IOException {
FileInputStream inputStream1 = new FileInputStream(new File("E:\\dev\\practice\\src\\charaster_test\\1.txt"));
FileInputStream inputStream2 = new FileInputStream(new File("E:\\dev\\practice\\src\\charaster_test\\2.txt"));
byte[] bytes1 = new byte[10];
byte[] bytes2 = new byte[10];
inputStream1.read(bytes1);
inputStream2.read(bytes2);
System.out.println("a字符的编码为:"+Arrays.toString(bytes1));
System.out.println("编码字符的编码为:"+Arrays.toString(bytes2));
}
}
运行结果:
从结果可以看出a字母在磁盘中被保存成了97的二进制形式
编字由-79和-32两个字节来代表,码字由-62和-21两个字节来代表
如果把文件保存为utf-8形式(前3位为标识符):
如果把文件保存为unicode形式(前2位为标识符):
字符的解码
基础的当字符需要通过显示屏进行显示给用户看的时候,以UTF-8编码为例:需要通过系统中UTF-8的解码方法把所有的字节码转化成Unicode字符集中的key值,然后查找Unicode字符集中对应的字形,最终交由显示屏进行输出。如果是Unicode解码,由于编码方式本来就是原生的Unicode字符集的key值,解码的时候就可以直接查找Unicode字符集中对应的字形。那么为什么还要有Utf-8这种二次编码的格式呢,详见下节内容。
常见的字符与字符编码
ANSI编码
基础的ascll字符集的长度为127位,只需要用7个bit位就能表示,但是计算机的存储器是以字节为单位的也就是8个bit,所以ascll字符编码的大小为1byte,最高位一般置为0.
ANSI编码是操作系统中默认的编码格式,中文操作系统中即使GBK编码
GBK编码
中国人自己的编码方式,主要是为了适应汉字在计算机中显示。
以前面的‘a’和‘编码’为例:
如果是ASCII中对应的字符则保持一致,编码范围为0-127并且用一个字节表示
如果是不是ASCII中的其他的字符,则用两个字节表示。第一个字节如果值大于二进制的01111111也就是大于127,就代表这个字符会用2个字节进行表示。第一个字节的范围为128~255,但是由于Java中的byte,其规定的范围为-128~127之间,最高位的bit位为标志位,1表示为+,0表示为-。所以第一个字节的十进制范围为128~254,十六进制为81–FE。第二个字节的十进制范围是64~126和128~254,十六进制范围为40–7E和80–FE
unicode编码
主要是为了适应计算机全球化,由于每个地方有不同的语言,如果要在计算机上显示不同的语言,所以就需要一个统一的字符集把世界上所有的字符储存起来,然后进行编码。
具体的编码方式:
https://baike.baidu.com/item/Unicode/750500?fr=aladdin
UTF-8编码
UTF-8就是在互联网上使用最广的一种unicode的实现方式,这是为传输而设计的编码,也就是为了减少unicode表示英文数据的冗余二设计出的基于unicode编码的编码方式。
具体的编码方式:
https://www.zhihu.com/question/23374078
Base64编码
基于网络传输的编码方式,可以把明文数据简单的变成不可读数据。
具体的编码方式:
https://baike.baidu.com/item/base64/8545775?fr=aladdin
乱码的实质
乱码的实质即是计算机用于显示字符文件时,也就是字符解码的时候用了和编码不同的方式进行解码,所以在解码时不能使用和编码时对应的的字符集,或者解码后不能取得和原有编码方式编码出来的key值。
参考: http://blog.jobbole.com/84903/
https://jingyan.baidu.com/album/6f2f55a15333dbb5b83e6c41.html?picindex=2