今日起兴搞搞文件与流的知识,刚起手写了个读取与生成文件的小程序,跑完以后,发现一张366kb的图像被代码给生生的写成了180kb大小的损坏文件?!我艹,这是几个意思?刚起手就被拍死了啊!
原始的代码:
FileInputStream in=null;
FileOutputStream out=null;
try {
in=new FileInputStream("C:\\Users\\Administrator\\Desktop\\promo\\20150417114.jpg");
out=new FileOutputStream("C:\\Users\\Administrator\\Desktop\\promo\\sub\\test.jpg");
while(in.read()!=-1){
out.write(in.read());
out.flush();
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
out.close();
in.close();
}
从逻辑上来看一点事都没有,可是在跑完了以后,与写之前设想的效果差的太远!本来就简短的代码,瞬间充满了陌生···,于是开始一句一句的推敲。首先关于输入和输出对象是没问题的,可以排除,继续,读取也是没有问题的,要不然也不会生成半拉莫名文件。问题于是就锁定在了write()方法上。查看outputstream的api,发现关于outputstream的write()方法的介绍很凝练,很简洁,就一句话,"将指定字节写入此文件输出流。"这是没问题的。不过后半句留下了线索,"实现outputstream的write方法"。好的,路没断就行,顺藤摸瓜,查看outputstream的write方法,"将指定的字节写入此输出流。write方法的常规协定是:向输出流写入一个字节。要写入的字节是参数b的八个低位。b的24个高位将被忽略。outputstream的子类必须提供此方法的实现"。也是很短的但是却很详细很关键的一句话,"b的24个高位将被忽略"。我了个大艹!问题已经很明确了,就是在文件传输过程中丢失了一些数据。是很明确了!可是这个高低位是个毛意思?还是不甚清楚啊。当然,其实,到了这,有两个选择,一是既然知道问题出现在哪里,就可以避免这个这个坑,就选择别的方法,不让数据丢失,另外一个就是去看看这丢失的数据到底是什么?有时候人的好奇心就是这么自然的出现的,很自觉的就去百度了,字节的高地位。看到很多不知道牛逼几何的人物的文章都说百度不咋滴,查资料都用谷歌什么的,我只想说你可以鄙视百度,但不能鄙视百度出来的结果。好的,结果出来了,话说有个叫字节序的名词,其含义很直白,就是字节的顺序。嗯,很白。通俗易懂到很通俗,嗯,懂了。不过更深的内涵就有点意思了,起码对于解决问题有了后续的线索。"大于一个字节类型的数据在内存中的存放顺序"。都说一入某某深似海,查资料也是这样,引出来的新名词又引出来新的名词,瞪着俩眼看吧,Big—Endia和little-Endia。Big-Endia,就是高位字节排放在内存的低地址端,低位字节排放在内存的高地址端。Little-Endia,就是低位字节排放在内存的低地址端,高位字节排放在内存的高地址端。我擦,有点晕,故事不到结束,那就还有剧情。且看着。网络字节序:4个字节的32bit值以一种神秘的次序传输,首先是0-7bit,然后8-15bit,再然后16-23bit,最后是24-31bit.嗯,很神奇。然后这种神奇的传输次序被有关行业称作大端字节序。到目前为止,对于解决问题我自认为还没什么进展。耐心,面对着急是脆弱的。跳一下看看···有时候吃坏东西拉肚子不是东西不能吃,而是我们的消化能力有问题。答案就在那里,偏偏看了还不懂,此时我懂了,为什么会有限制级这个名词了,因为有些东西可能看了也不懂。好的,心态调好了,就再换一份看,等到消化能力上去了,再看吧。看着看着,发现查出来的资料都不能解我心头之惑。反而牵扯出来新的东西开始在脑海里打转,查资料的解决方式到此应该打住了,不管新的问题有多大,都不能湮灭最初的起点。我只是想知道b的24个高位是啥玩意?我就是想知道我写出的文件为啥无辜的成了半拉?答案已经出来了,只是我看不懂而已。东西能吃,拉肚子只是我消化能力不行而已。为啥文件成了半拉?因为调用的方法将读入的数据在写出的时候丢了一部分。好的,勉强也算是完成了逻辑上的圆满。好,既然write方法会丢失数据,那么有没有别的方法解决问题呢?嗯,有的。除了write()方法不是还有write(byte[] b)吗?这么一改,用一个byte数组去接数据,果然就好了
byte[] b=new byte[1024];
while(in.read(b)!=-1){
out.write(b);
out.flush();
}
这又是什么情况?这到底是什么情况?查看outputstream的源码:
public void write(byte b[], int off, int len) throws IOException {
if (b == null) {
throw new NullPointerException();
} else if ((off < 0) || (off > b.length) || (len < 0) ||
((off + len) > b.length) || ((off + len) < 0)) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return;
}
for (int i = 0 ; i < len ; i++) {
write(b[off + i]);
}
}
write(byte[]b)和write(byte[] b, int off,int len)最后走的都是上边的方法。到了这里我突然很好奇我的in.read()方法到底读了些什么玩意儿?把in.read()打到控制台输出的是一大堆没有规律的数字,把in.read(b)打到控制台,是清一色的1024,哦,最后一个是683。这是几个意思?瞬间我感觉是这个read()方法出了问题同样的走到了顶层的InputStream里边的是个抽象方法,谁用谁重写。那就看Read(byte[]b)
public int read(byte b[], int off, int len) throws IOException {
if (b == null) {
throw new NullPointerException();
} else if (off < 0 || len < 0 || len > b.length - off) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return 0;
}
int c = read();
if (c == -1) {
return -1;
}
b[off] = (byte)c;
int i = 1;
try {
for (; i < len ; i++) {
c = read();
if (c == -1) {
break;
}
b[off + i] = (byte)c;
}
} catch (IOException ee) {
}
return i;
}
返回的是个int?返回的是i?返回的是个长度?那我刚才打出的read(byte[]b)的就是文件的size?我统计了一下,打出了366个1024。我晕了,懵了,愣了,傻了,蛋疼了···各种心酸各种苦涩,我读书少,我好怕,折腾了这么一大圈,我还是不能理解我那个183kb大小的文件到底是怎么出来的,而我更不能理解的是为什么换了个方法就可以输出正确?那个read(byte b[], int off, int len)方法里的for循环到底是有怎样的魔力?只不过是把读到的字节按顺序附加到了数组里而已。这尼玛?····哎,有点意思啊?根据打出的read()和Read(byte[]b)结果对比,好像有点启发,read()方法每次读取一个字节,read(byte[]b)每次读取b.length大小字节,不不不,read()方法读取的数据有问题,大大的有问题,我直接循环打出read()方法,竟然是无数个255,跑了三分钟也没停止。这个不是重点,重点是255,255,虽然不太清楚是什么含义,但是我就是认为它有问题,诬陷有罪,怀疑合理。我就是怀疑它有问题,嗯,有问题,图片的存储方式是什么的?read()读到的到底是什么玩意儿?!我使用的是jpg格式的图片,百度出来的东西让我开始疼,头疼,蛋疼,全身疼···,没事,没事,只要顺着线索总会有结束的真相。不对,255=2^8-1也就是一个字节?!哈哈,我找到了
FileInputStream in=null;
FileOutputStream out=null;
int a=0;
try {
in=new FileInputStream("C:\\Users\\Administrator\\Desktop\\promo\\20150417114.jpg");
out=new FileOutputStream("C:\\Users\\Administrator\\Desktop\\promo\\sub\\test.jpg");
while(in.available()>0){
out.write(in.read());
out.flush();
}
/* byte[] b=new byte[2048];
while(in.read(b)!=-1){
out.write(b);
out.flush();
}*/
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
System.out.println(a);
out.close();
in.close();
}
}
我把猜想跑了一遍,果然,结果是正确的!有时候,问题越研究越被复杂,其实,最初的问题,很简单。重新梳理一下,首先创建一个输入对象,然后按每次一字节读取
in=new FileInputStream("C:\\Users\\Administrator\\Desktop\\promo\\20150417114.jpg");
out=new FileOutputStream("C:\\Users\\Administrator\\Desktop\\promo\\sub\\test.jpg");
int c=0;
while((c=in.read())!=-1){
out.write(c);
out.flush();
}
这样是没问题的,按最最开始的代码跑的话,最后的图片只有183kb大小,问题就出在
while(in.read()!=-1){
out.write(in.read());
out.flush();
}
人最容易忽略的地方就是明明脑子里是这样想的,手却不是这样做的!仔细,看api,"从输入流中读取一个数据字节如果没有输入可用,则此方法将阻塞",下面还有一句,"返回:下一个数据字节;如果已达文件末尾,则返回-1",就是这样一句话,导致了图片的数据缺失!当条件in.read()!=-1,调用了一次read(),此时输入流是刚刚好对应的是图片的假设是第一个数据点,而写入到输出流时又调用了read()方法,此时数据已经是图片的第二个数据点了!所以整个程序跑完,图片就这样被一次少一个数据点,一次少一个的给无辜的裁剪了···为了验证猜想,
while(in.read()!=-1){
out.write(in.read());
out.flush();
}
这样跑完,生成的图片是183kb大小,
while(in.read()!=-1){
System.out.println(in.read());
out.write(in.read());
out.flush();
}
这样跑完,图片的大小成了122kb大小
while(in.read()!=-1){
System.out.println("===="+in.read());
System.out.println("---"+in.read());
out.write(in.read());
out.flush();
}
这样跑完,图片的大小成了92kb大小
while(in.read()!=-1){
System.out.println("=="+in.read());
System.out.println("--"+in.read());
System.out.println("-+-"+in.read());
out.write(in.read());
out.flush();
}
这样跑完,图片仅剩72kb大小,这不是搞了压缩,这是数据丢失!丢失!什么是真相?这就是真相!要说贱人是怎么犯贱的,我想他们都是很自然的把原本应该不自然的事情给做了出来,还做的洋洋得意,然后一个人贱贱的慨叹高处不胜寒,贱贱的慨叹独孤求败了···
int c=in.read();
while(c!=-1){
out.write(c);
out.flush();
}
刚把数据丢失的问题给解决我只是挪了一下read的位置,问题又来了,图片的size开始猛涨,像嚼了炫卖似的停不下了!一兆,两兆···我艹,这是几个意思?!在往控制台输出,255,又见255!无数个草泥马般的255蜂拥着奔袭控制台,停不下来!根本停不下来!这是好事,一部好的小说,挖了坑,埋了伏,就要有个交代,没错,这个255就是龙套的255=2^8-1,没错,就是它!话说有时候一个问题解决了,所有的问题就都不是问题了。如果我没猜错,这个255是图片的第一个数据点,而打出的无数个255全部都是第一个数据点,c=in.read();只读了一个字节,然后就开始进入了死循环···阿弥陀佛,至此,最初的问题,是已经解决了。可是,因果轮回,无意间种下的因,不知何时会结成孽果···什么是字节序?不是说b的24个高位将被忽略?图片的存储方式?唉,生命不息,折腾不止···