zoukankan      html  css  js  c++  java
  • 我真傻,真的,我单知道...

    背景

    公司做的一个气象数据显示项目,其中涉及到很多原始格式的数据解析,比如格点气象数据,内部数据一般就是二维数组,在存储的时候,一般采用二进制方式进行存储。

    格点数据的本质,可以理解成一个图片,每一个像素点上有一个数据值。

    任务

    我接手做这个解析工作,就是要将二进制格式的数据,转化为更为通用的文本格式,方便查看和显示。
    另外一项附加任务,则是因为原始数据的密度较高,是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跳过的长度也会不够。
    所谓基础不牢,地动山摇,加强学习加强基础很重要!

  • 相关阅读:
    主流NoSQL数据库评测之HBase
    Java Io原理及应用
    Facebook:HBase每月存储1350亿条信息
    Facebook为何选择云计算开源Hadoop
    ActiveMq配置解析
    主流NoSQL数据库评测之HandlerSocket
    软件类官方网站收藏
    Mongodb 更新失败解决方案
    主流NoSQL数据库全方位评测:MongoDB
    java为什么比c++要慢?以及如何提高java的效率
  • 原文地址:https://www.cnblogs.com/binblog/p/13695247.html
Copyright © 2011-2022 走看看