我真傻,真的,我单知道...
背景
公司做的一个气象数据显示项目,其中涉及到很多原始格式的数据解析,比如格点气象数据,内部数据一般就是二维数组,在存储的时候,一般采用二进制方式进行存储。
格点数据的本质,可以理解成一个图片,每一个像素点上有一个数据值。
任务
我接手做这个解析工作,就是要将二进制格式的数据,转化为更为通用的文本格式,方便查看和显示。
另外一项附加任务,则是因为原始数据的密度较高,是1公里x1公里的密度,其纵向有435公里,横向有355公里,总共涉及435x355=154425个点,由于前端优化不太给力,只能对密度降级,变成5公里x5公里的密度。
解析代码
所谓二进制数据,就是将数据一个一个往后面码,所以代码也不难,劈里啪啦一阵敲,就完成了:
// InputStream in; 流对象
float[][] data = new float[HEIGHT][WIDTH];//使用float数组接收数据
byte[] buf = new byte[2];//buf
BufferedInputStream bin = new BufferedInputStream(in);//使用缓存流对象
for(int y=0; y<HEIGHT; y++) {//从左上,逐行读取
for(int x=0; x<WIDTH; x++) {
data[y][x] = read(bin, buf);//读取一个数据
}
skip(bin,WIDTH*5*2*4);//往下跳过4行
}
问题
数据本来的样子是:
我解析出来之后,将结果放入到显示界面中查看,解析的结果最终出来却是条纹状的数据。
而令人崩溃的是:
但是如果我没有进行密度降级的时候,又是正常的(将原始宽度、高度逐一解析,而不进行跳行)。
过程
中间是一个痛苦的试错过程,我尝试打印当前流的位置,因为这个形状看起来像是一个错位导致的,然而我通过一个position去记录,发现是正常的。
转机
在通过的过程中,我时不时的告诉自己,这一定是哪些写的有问题。
一个偶然的想法,我在创建缓存流的时候,加入一个参数size=3550(刚好是5行的大小)
BufferedInputStream bin = new BufferedInputStream(in, 3550);
诶!
这就是我想要的。
让我不禁想起那首歌——《我的滑板鞋》。
原因
其实很简单,问题就出现在
skip(bin,WIDTH*5*2*4);//往下跳过4行
而我是这样写的(这样写,是为了避免抛出checkedException)
try {
inputStream.skip(n);
}catch (Exception ex){
throw new RuntimeException("reading data error:", ex);
}
也就是我以为,这个skip会确保真实跳过所需要的字节数,然后查到BufferedInputStream的skip方法
public synchronized long skip(long n) throws IOException {
this.getBufIfOpen();
if (n <= 0L) {
return 0L;
} else {
long avail = (long)(this.count - this.pos);
if (avail <= 0L) {
if (this.markpos < 0) {
return this.getInIfOpen().skip(n);
}
this.fill();
avail = (long)(this.count - this.pos);
if (avail <= 0L) {
return 0L;
}
}
long skipped = avail < n ? avail : n;
this.pos = (int)((long)this.pos + skipped);
return skipped;
}
}
可以看出,BufferedInputStream并不会确保跳过所需要的字节数——如果所跳过的字节超过当前的缓存长度,则只会跳到当前缓存的末尾。
由此,我以为它跳到了X位置,实际上它还在原来的地方——所以,这也是为什么条纹状会出来的原因。
解决
知道原因,解决办法就比较多了
- 调整参数方法:就是上面写上3550作为参数,确保刚好跳到指定的位置;
- do-while循环确保:当小于跳过数时,继续往前跳
do{
n -= inputStream.skip(n);
}while(n>0);
- 不使用BufferedInputStream
直接使用原始的FileInputStream读取并不会存在这个问题,当skip在最终的字节流上进行移动时,会真实有效。
最后采用:3550参数,同时为防止将来可能出问题,也做了do-while
的判断。
总结
如标题所说,我真傻,真的,我单知道InputStream.read,可能会读取的长度可能会不够,可是我却不知道skip跳过的长度也会不够。
所谓基础不牢,地动山摇,加强学习加强基础很重要!