乱码的原理解析(附带java编码原理解析)

        FileOutputStream fileOutputStream =
                new FileOutputStream("module-11\\data\\out.txt");
        fileOutputStream.write(100);
        fileOutputStream.write(35);

        FileInputStream fileInputStream =
                new FileInputStream("module-11\\data\\out.txt");
        System.out.println(fileInputStream.read());// 100
        System.out.println(fileInputStream.read());// 35

先创建了一个out.txt的文件,写入两个字节的内容。因为文件最基本的格式就是01二进制的样子,所以这个out.txt,里面存放的就是100和35的二进制数据。用 binary viewer打开

用binaryViewer打开out.txt,查看里面的二进制数据。可以看到:

64 是16进制数,占一个字节,转化二进制:01100100 刚好是100,

23 是16进制数,占一个字节,转化二进制:00100011 刚好是 35,

这也验证了我刚刚确实在out.txt里存入的100和35是按二进制存储的。

常说的 用UTF-8,GBK编码 打开文件注意是打开文件)是什么意思呢?他们只是读取这基本的二进制数据,不会去改变数据本身。他们根据自己的编码规则,会去读取字节数据,然后把他转化为字符。在这里ASCII编码,就读取了01100100,然后根据ASCII的编码,这个二进制对应的字符就是d,同理,00100011对应字符#。

打开文本文件,肯定是要选编码格式,编码格式不同,打开得到的字符有时候就不同

但是 我们无论以什么编码打开out.txt源文件,得到的二进制数据肯定还是我们刚刚存入的01100100,00100011

FileInputStream fileInputStream =
 new FileInputStream("module-11\\data\\out.txt");
System.out.println(fileInputStream.read());//100
System.out.println(fileInputStream.read());//35

 我们重新读取文件 可以看到,数据源并没有发生改变

         反过来,我们创建一个txt文件,然后写入 汉字 “中”,我们保存的时候,可以选择不同的编码保存注意这里是保存文件),编码不同,保存的文件结果,基本也不同(这里的文件结果不同,指的是文件的二进制数据不同),所以这就形成了我们常见的 乱码(用A 编码保存文件,以B编码打开文件)。所以我们保存文件,是用什么编码,那么打开文件一定也要是对应的编码格式打开,否则就形成乱码。

        

关于Unicode和GBK,UTF-8,UTF-16的关系解释:

Unicode是用来存储全世界的语言的一张映射表,全世界所有的字符都被Unicode编码,GBK,UTF-8,UTF-16是Unicode的实现方式之一。 

看看UTF-8的编码方式,(UTF-8是对Unicode进行编码,举个例子:  这个字符如果保存为Utf-8格式的文件,软件编码的顺序为 人 ->  Unicode编码->UTF-8编码 ),中间这道编码有唯一性,因为这个是 全世界统一的编码规范,为什么要有这 Unicode编码->UTF-8编码 这道编码顺序呢,为什么有个全世界统一的编码规范,而还要去扩展编码规范呢?这个下一篇文章再说!

        找了两个Unicode字符,但是好像在很多地方都不支持,表现出复制后为乱码(这里要特别注意,这两个字符虽然是乱码,只是因为软件不支持这两个Unicode 的图像化),分别以UTF-8格式 保存在文本里。

  • 𐆙    U+10199

  • 𐆘    U+10198
     就这样复制了以UTF-8 另存 就行了,然后然后查看二进制文件。

  • 这两个字符只是软件不支持图像化,但是编码依然是UTF-8 的编码,见下图。

假设我们在一个window桌面建立一个 .txt 文件,然后在里面写入文件,这个时候可以保存文件为任意的格式,根据格式的不同,这个文件的二进制的数据也不同,因为编码都是有一定的规则。我们可以想象每一个写入的 字符 都是 U+????? (Unicode码点),然后就是根据不同的编码方式,把这些 字符转化为01二进制的计算机存储的数据。这个是好理解的。

但是在java里面这个就难理解了,首先在 java内部(这个内部指的是在JVM里面)是用的UTF—16这种编码格式,比如说一个特殊的字符串

"a我𝕆" 字符转换为UTF—16 格式,在这里就是 \u0061\u6211\uD835\uDD46 (\u0061代表这个是一个UTF—16 码元 ),每个 char 都只能存储这一个\u0061 码元,因为这是0x0061(16进制数)有2个字节,在java里char类型是占两个字节。𝕆 字符比较特别,2个 UTF—16 码点 才能表示他,所以这个字符要2个char来存储,所以这整个 "a我𝕆" 的string的长度为4 ,因为他用了4个 char 存放(但是好像是jdk1.8 后 String类 就不是用 char[] 来存放数据,而是用 byte[] 来做为存储的容器。我们这里还是假设用char[] 来做String的容器,其实用 byte[] 来做容器了,你也可以把它内部当作char[])

插入一条例子

OutputStreamWriter outputStreamWriter = new OutputStreamWriter(
new FileOutputStream("module-11\\data\\UTF_8.txt"), StandardCharsets.UTF_8); 
//其实默认的就是UTF-8编码 格式写入文件
String s = "\u0061\u6211\uD835\uDD46"; // a我𝕆
char[] chars = s.toCharArray();
System.out.println(chars.length);//4

outputStreamWriter.write(chars[3]);
outputStreamWriter.write(chars[2]);
outputStreamWriter.write(chars[1]);
outputStreamWriter.write(chars[0]);
outputStreamWriter.flush();
//  存储的结果: ??我a

第二次顺序存储:
//        System.out.println(chars.length);//4
//        outputStreamWriter.write(chars[0]);
//        outputStreamWriter.write(chars[1]);
//        outputStreamWriter.write(chars[2]);
//        outputStreamWriter.write(chars[3]);
//        outputStreamWriter.flush();
//        //  存储的结果: a我𝕆

write方法 只能每次读入一个 char ,在这里所有的文本在被读入后,会被 重新进行一次转化,就是UTF—16 到 Unicode的转化,这里只能这样理解,才能解释这里两次存储的差异性。

读文件就是反过来,

可以看到二进制的数据,当编码确定就可以直接转化为字符,这里的3个字节,是UTF-8的编码规则

byte[] bytes1 = new byte[3];
bytes1[0] = (byte) 0b11100110;
bytes1[1] = (byte) 0b10001000;
bytes1[2] = (byte) 0b10010001;
String s = new String(bytes1, StandardCharsets.UTF_8);
System.out.println("s = " + s);// s = 我
FileOutputStream fileOutputStream = new FileOutputStream("byte.txt");
fileOutputStream.write(bytes1);
fileOutputStream.flush();//存储的结果也是 “我”

第一次写csdn,不太会,希望能多多包涵,也希望能提点意见

  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值