学习文件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,所以复制的图片数据就发生了改变。
反过来对于字节流来说,我一个字节一个字节的读取,我没有什么编码表,读到多少就是多少,这样图片的数据不会改变,也就能够实现复制。
最开始一直觉得字符流就是两个字节两个字节读取,后来才发现自己一直理解错了。也算是解决了一个困惑自己很久的问题吧,希望对大家也有所帮助。