计算机系统-从原理深度剖析:文件、网页、接口数据为什么会乱码

本文详细探讨了字符集和字符编码的概念,包括ASCII、GBK、Unicode和UTF-8等常见编码方式。通过举例说明了字符如何在计算机中存储和转换,并解释了乱码产生的原因。此外,还提到了Base64编码在传输中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

字符集与字符编码

字符集     

    计算机存储信息都是通过二进制的来进行存储,试想:计算机如何是显示图像的?计算机显示图像的时候是通过一个一个的像素点来绘制出来的。计算机是怎么知道如何绘制每一个字体的呢。答案就是:计算机通过字体的像素矩阵来对应绘制出的字体。那么如果在存储文件的时候储存的是一个像素矩阵的机器码表示 ,比如说存储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


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值