编码和解码

关于编码和解码,下面举一个例子

(1)查找码点并转换为二进制

比如'我'这个中文字符,在Unicode字符集中的码点是25105,将其转换为二进制是110,0010,0001,0001。

(2)编码

其二进制并不能直接存储到内存中,而是需要按照UTF-8的编码方式使用3个字节进行存储,格式为1110xxxx 10xxxxxx 10xxxxxx,可以看到余下16位二进制需要填充,所以将110,0011,1110,0001的高位补0,补成16bit,即0110,0010,0001,0001,填充到3个字节中去就是:11100110 10001000 10010001。这就是实际存储在计算机中的数据。

通过getBytes()方法对字符进行编码,也可以指定编码方式,下面是测试代码:

public class test1 {
    public static void main(String[] args) throws IOException {
        byte[] bytes = "我".getBytes("UTF-8");
        for (byte b : bytes) {
            System.out.println(b);
        }
    }
}

输出结果:

分析:getBytes()方法的返回值是字节数组。

-26的补码是11100110,可以看到与编码的第一个字节对应,后面一一验证也都是正确的。

(3)解码

首先读取3个字节的数据,将红色的bit位剔除,0110,0011,1110,0001就是实际的数据。

(4)转换为十进制

0110,0011,1110,0001转换为十进制就是25105。

终这个字符在GBK字符集中的码点为32456,转换为二进制后的结果为111,1110,1100,1000。

高位补1。

思考了N多问题。

问题1:read()方法的返回值一定是某个字符在字符集中的码点吗?

思路:①先把'我'这个字符按照UTF-8的编码方式写到文件中;

②再使用read()方法读取,并指定解码方式为UTF-8。

下面是代码实现:

public class test2 {
    public static void main(String[] args) throws IOException {
        byte[] bytes = "我".getBytes("UTF-8");
        FileOutputStream fos = new FileOutputStream("demo.txt");
        fos.write(bytes);
        fos.close();
        FileReader fr = new FileReader("demo.txt");
        int i = fr.read();
        System.out.println(i);
        fr.close();
    }
}

输出结果为25105,Unicode字符集中的码点相同,所以read()方法的返回值一定是字符集中的码点。

问题2:'我'这个字符在GBK中的码点?

由于上面的结论,即read()方法的返回值一定是字符集中的码点,所以下面依然利用上述代码试图给出'我'这个字符在GBK中的码点。

public class test2 {
    public static void main(String[] args) throws IOException {
        byte[] bytes = "我".getBytes("GBK");
        FileOutputStream fos = new FileOutputStream("demo.txt");
        fos.write(bytes);
        fos.close();
        FileReader fr = new FileReader("demo.txt", Charset.forName("GBK"));
        int i = fr.read();
        System.out.println(i);
        fr.close();
    }
}

输出结果是25105,就以为'我'这个字符在GBK字符集中的码点也是25105,和Unicode字符集中的码点一致。

在知道了'我'这个字符在GBK中的码点之后,我就试图利用GBK的编码方式自行计算出应存储在底层中的二进制,然后和通过getBytes()方法得到的字节进行对比,以为结果应该是一样的。

问题3:利用GBK的编码方式自行计算出应存储在底层中的二进制

25105的二进制是110,0010,0001,0001,按照GBK的两个字节中第一个字节的首位为1的规则,在最高位补1,结果是1110,0010,0001,0001。

问题4:通过getBytes()方法得到的字节

public class test1 {
    public static void main(String[] args) throws IOException {
        byte[] bytes = "我".getBytes("GBK");
        for (byte b : bytes) {
            System.out.print(b + "  ");
        }
    }
}

输出结果为-50和-46:

下面来分析一下这两个数:

计算机存的是补码,但我们打印出来的是十进制。这个十进制是补码所对应的十进制。

那么补码所对应的十进制是如何计算的呢?

拿补码1111 1111来进行举例。

补码:1111 1111 --> 反码:1111 1110 --> 原码:1000 0001,转换为十进制是-1。

所以要计算补码所对应的十进制,首先把补码转成反码,再把反码转换为原码,最后把原码转成十进制数。

一般来说,补码只是原码的另一种表现形式。补码和原码所表示的十进制的值是一样的。

那么整数所对应的补码又如何计算呢?

-50的原码是1011 0010,-46 的原码是1010 1110。

步骤如下:

原码:1011 0010 --> 反码:1100 1101 --> 补码:1100 1110

原码:1010 1110 --> 反码:1101 0001 --> 补码:1101 0010

将两部分拼在一起之后为1100 1110 1101 0010,这就是'我'这个字符使用GBK编码方式实际存储在计算机中的二进制,转换为十进制是52946,这就是'我'这个字符在GBK字符集中所对应的码点。

-50和-46的补码是1100 1110 1101 0010,这是在计算机中实际存储的,和我计算出来的1110 0010 0001 0001并不一样啊。百思不得其解,为什么不一样?因为25105并不是正确的码点,所以编码的二进制1110 0010 0001 0001自然也就是错的。

至此可以得出read方法返回的都是字符对应的Unicode码点。

问题5:已经知道了'我'这个字符在GBK中的码点是52946,想验证一下对不对呢?

52496转换为十六进制是CED2,通过在线GBK编码表-GBK中文字符集-在线GBK汉字编码字符集对照表 (jsons.cn)

在这个网址中进行查询(快捷键crtl+F)得到CED2对应的字符就是'我',下面是图片。

现在可以回答这个问题了:

问题6:如何知道某个字符在某个字符集中的码点?

有两种方式:①查字符集;

②通过getBytes()方法得到存储在计算机中的数据,然后解码再转换为十进制,就是对应的码点了。这种方式一定要了解编码方式,要不然不会解码。 

问题7:GBK的两个字节中第一个字节的首位为1,是符号位还是数值位?

-50和-46的补码是是1100 1110 1101 0010,这是实际存储在计算机中的数据。

(1)首先把最高位的1看作数值位,转换为十进制是52946;

(2)如果看作是符号位,那肯定是负数呀,但是字符集码点怎么会是负数呢?

所以结论是GBK的两个字节中第一个字节的首位为1,是数值位。

问题8:GBK编码方式为什么第一个字节的首位是1?

我觉得是因为中文字符的码点都很大,都超过1000 0000 0000 0000(其十进制是32,768),又搜了GBK的码点范围,给出的十六进制范围是0x8140~0xFEFE,转换为十进制是33088~65278。

再次给出一个建议,不要再使用GPT进行进制转换了,都是错的。

可以看到GBK的码点的开始值33088是大于32768的,所以这也就是为什么最高位为1的原因,并不是人为设置的。

问题9:关于1100 1110 1101 0010转换为十进制

(1)刚开始使用GPT进行进制转换,下面这个将1100 1110 1101 0010最高位的1解释为了符号位。

就不能不解释为符号位吗?于是又用GPT指定首位不是符号位,得到的结果是50792,我就一直以为1100 1110 1101 0010转换为十进制是50792。

补充:好像GPT还给出过47100,真是离谱。

但是我又多想了一步,用网址验证一下50792转换为二进制的结果,下图可以看到和实际存储在计算机中的二进制1100 1110 1101 0010并不一样。为啥总是对不上啊,不知道哪个有问题,怀疑是GPT有问题,烦死。

到底是谁有问题? 答案就是GPT,总是瞎给答案。

(2)问题10:二进制转换为正确的十进制是如何得到的?

因为后面GPT又指出可以使用Java编程语言进行转换,代码如下,我为什么那么相信编程的结果,因为我相信程序是不会骗人的。

public class test3 {
    public static void main(String[] args) throws IOException {
        String binaryString = "1100111011010010";
        int decimal = Integer.parseInt(binaryString, 2);
        System.out.println("十进制数是: " + decimal);
    }
}

输出结果就是52946。

  • 28
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值