java不能使用字符流读取二进制文件(如图片)的原因

本文通过实例探讨了使用字符流读取和写入二进制文件(如图片)导致的问题。由于字符流默认采用UTF-8编码,无法正确处理所有字节序列,当遇到无法映射的字节时,会被替换为Unicode的'替换字符'(U+FFFD,十六进制EFBFBD)。这解释了文件大小变化和图片无法打开的原因。相比之下,字节流按原始字节操作,确保了二进制数据的准确性。理解编码与流的区别对于避免此类问题至关重要。
摘要由CSDN通过智能技术生成

学习文件IO的时候,听老师说读取文本推荐用字符流,但是读取二进制的文件不能用字符流只能用字节流。自己编写程序测试的时候发现确实这样,利用字符流读入一张图片然后再利用字符流输出,新的图片无法打开,而且图片的大小还改变了。
虽然能够理解利用字符流读取二进制文件确实可能会出一些问题,但是为啥大小都改变了??
网上看了几篇文章感觉都说的云里雾里的,所以最终还是决定好好研究一下原因。

@Test
public void charGetFile() throws IOException {
    FileReader fr = new FileReader("e:\\a.png");
    FileWriter fw = new FileWriter("e:\\b.png");
    int data=0;

    while((data=fr.read())!=-1){
        fw.write(data);
    }

    fr.close();
    fw.close();
}

上述代码将a.png复制为b.png,得到图片如下
在这里插入图片描述
文件大小改变了,自然b.png也无法打开。
然后我分别查看了两张图片的16进制数据。
这是图片a的部分数据内容
在这里插入图片描述

这是图片b的
在这里插入图片描述
仔细看会发现,部分数据是对得上的,但是有的数据比如(89 D0)会变成EF BF BD
很明显这就是罪魁祸首了,也可以解释为什么图片大小会变大以及图片会打不开。
那么这个EF BF BD到底是个啥呢?
在解释之前可以先看看这篇文章,因为对这个现象会有疑惑还是因为编码没有搞明白。
java通过字符流读写文件,默认是按照utf-8读写的。也就是说在读取图片a的时候是按照utf-8的格式读取的,但是通过我推荐的那篇文章可以知道utf-8的编码方式只有四种:
0xxxxxxx
110xxxxx 10xxxxxx
1110xxxx 10xxxxxx 10xxxxxx
11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
第一个字节的值是89—二进制是 1000 1001.结果一查表发现,没有这一号人啊
当写入的整数无法在字符集编码中找到对应的字符时,字符流读取会将其转换成Unicode Character ‘REPLACEMENT CHARACTER’ (U+FFFD):65533 参考这篇文章
也就是说想读取第一个字符,然后发现不认识这个字符,就会转为FFFD(这是unicode编码)
然后我之前也说了写入文件的时候也是默认utf-8格式。所以将FFFD转换为utf-8就是
11101111 10111111 10111101对应的16进制就是EF BF BD!(转换规则文章里面也有)
读到这里我相信大家已经明白了,对于图片来说每一个字节代表的就是一个像素值那么0-255都有可能取到。但是对于一个编码方式来说有的值是取不到的(比如uft-8就不知道1xxxxxxx对应的值)。当某一个值在编码表里面找不到的时候,对于unicode来说就会返回65533,所以复制的图片数据就发生了改变。
反过来对于字节流来说,我一个字节一个字节的读取,我没有什么编码表,读到多少就是多少,这样图片的数据不会改变,也就能够实现复制。
最开始一直觉得字符流就是两个字节两个字节读取,后来才发现自己一直理解错了。也算是解决了一个困惑自己很久的问题吧,希望对大家也有所帮助。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值