• 利用C一种最有效的文件存储方式——16bit有符号位2进制存储


    2020年7月6日  23:17:31

    环境:VS2013   C语言

    最近在处理一些较大的数据,今天想要将一组超过百G的信号采集数据进行信号处理后,在重新存储起来,作为后续程序的起始数据。

    在看了网上诸多的什么比如用类人算的方法将整数拆解为2进制、直接存储整数格式等建议之后,我感觉这些都不是我想要的。

    受到读取数据16bit有符号数据的启发:(如下,小端模式)

    我也想将存储的数据也得到类似于此的性能,即存储数据的大小SizeF严格遵循

    SizeF = 2(Byte)* Fs * t

    其中Fs为采样率,t为采样时间。

    但是使用整形数据直接储存或16进制直接储存并不能满足。

    最后我将本问题的解决方案定为:也就是想要读取一组格式为小端模式的16bit有符号数据,并将其进行信号处理后,重新以最有效的16bit有符号数据格式存储(大端模式)起来。。最后也实现了。

    一开始的测试,用了一个大小(393750kB)的原数据做例子:

    以上为测试的结果,从上到下分别以5位整数(%5d)、4位16进制(%04x)以及16位2进制的不同方式存储。

    可以很明显的看出存储方式对空间利用的优化。

    其实实现的代码也很简单:

    对小端模式的16bit读取重组(作者电脑C以大端模式读取数据),代码如下:

    int tek_read(int i_offset, long long int offset, int readSum)
    {
        FILE *odata = NULL;
        char *obuf = NULL;
        size_t bytes_read;
        //int16_t mid_data[DATA_BUF_SIZE];
        int16_t *mid_data;
        mid_data = (int16_t *)calloc((2 * readSum), sizeof(int16_t));
        int16_t temp = 0x00ff;
        int16_t temp1 = 0x0000;    //用来屏蔽污染中间变量,详情见下
        int i, j, l;
        int d, length = 0;
        int flag = 10;
    
        if (mid_data == NULL)
        {
            return FAILURE;
        }
    
        odata = fopen(DATA_name, "r+");
        if (odata == NULL)
        {
            free(mid_data);
            return FAILURE;
        }
        obuf = (char *)malloc((readSum * 2) * sizeof(char));    //动态分配内存 储存连续readSum *16b 数据
        if (obuf == NULL)
        {
            free(mid_data);
            free(obuf);
            return FAILURE;
        }
        long long int t_offset;
        t_offset = i_offset * offset * 2;
        flag = _fseeki64(odata, t_offset, 0);
    
        bytes_read = fread(obuf, 1, (2 * readSum), odata);
    
        fclose(odata);
    
        if (bytes_read < (2 * readSum + 1))
        {
            for (i = 0; i < (2 * readSum); i++)
            {
                mid_data[i] = obuf[i];
            }
            memset(Iri_data, 0, (2 * readSum));    //将Iri_data元素换成0
            for (j = 0; j < readSum; j++)
            {
                Iri_data[j] = mid_data[2 * j + 1];
                Iri_data[j] <<= 8;
                temp1 = mid_data[2 * j] & temp;    //某些低8位(类型是16位)的前8位读成ff导致或运算之后最终前8位成为ff,
                //为避免污染,先与0x00ff与运算
                Iri_data[j] |= temp1;
            }
    
            free(obuf);    //读到Iri_data中后就释放内存
            //数组长度
            d = 0;
            l = 0;
            //可能有连续5个0????末尾是0导致的问题
           free(mid_data);        //释放中间内部
            return length;
        }
        else
        {
            printf("* I am sorry to tell u there is a trouble                    
    in taking Tektronix DATA!
    Please cheak your settings!  
    ");
    
            free(mid_data);        //释放中间内存
            return 0;
        }
    }
    中间的信号处理就不详细介绍了
    最后的大端模式将16bit有符号数据
    写入文件,实现代码:
    int outfile(void)
    {
    
        FILE *fp_out;
        fp_out = fopen(outname, "a+");
        for (int i = 0; i < length_90ms_7Mhz; i++)
        {
            int16_t temp = Iridium_result_D16[i];
                    //大端模式
            fputc((temp >> 8) & 0xFF, fp_out);
            fputc(temp & 0xFF, fp_out);
        }
        fclose(fp_out);
    
        return SUCCESS;
    }
    
    

    对于我200个G的原始数据,预处理效果很理想。

    存储数据效果如下:

     

     

    2020年7月7日18:13:34 sun 修改
    在使用整个程序发现了一些问题并相应的进行了一些修改,记录如下。
    首先,由写入数据部分的启发,将读取原数据部分(即读取小端模式,16bit有符号数据)修改为:

    int data_read(int i_offset, long long int offset, int readSum)
    {
        FILE *odata = NULL;
        int16_t mid_data = 0;
    
        int16_t temp = 0x0000;    //用来作为中间变量,详情见下
        int length = 0;
        int flag = 10;
        
        odata = fopen(DATA_name, "r+");
        if (odata == NULL)
        {
            return FAILURE;
        }
    
        long long int t_offset;
        t_offset = i_offset * offset * 2;
        flag = _fseeki64(odata, t_offset, 0);
    
        memset(Iri_data, 0, readSum);    //将Iri_data元素换成0
        for (int i = 0; i < readSum; i++)
        {
    
            char obuf0 = fgetc(odata);
            char obuf1 = fgetc(odata);
            Iri_data[i] = obuf1;
            Iri_data[i] << 8;
            Iri_data[i] &= 0xff00;
            temp = obuf0 & 0x00ff;
            Iri_data[i] |= temp;
    
            length = i + 1;
        }
    
        fclose(odata);
    
        return length;
    }

    在以上的写入数据程序,我发现写入数据的文件大小总是和理论上的不同,数据的实际大小总是略大于计算的大小,而用查看文件时其数目也与我写入的数据不符,查了一圈资料,发现了问题,要用2进制(wb)方式写入:

    int data_outfile(int i_offset, long long int offset, int writeSum)
    {
    
        FILE *fp_out;
        fp_out = fopen(outname, "ab+");
        int flag = 10;
    
        for (int i = 0; i < writeSum; i++)
        {
            int16_t temp = Iridium_result_D16[i];
    
            //大端模式
            fputc((temp >> 8) & 0xFF, fp_out);
            fputc(temp & 0xFF, fp_out);
        }
        fclose(fp_out);
    
        return SUCCESS;
    }

    ab+是在文件末尾以2进制格式写入,
    类似于上上部分,读取自己写入的数据的程序如下,注意(rb+):

    int data_read(int i_offset, long long int offset, int readSum)
    {
        FILE *odata = NULL;
        size_t bytes_read;
        int16_t mid_data = 0;
        int16_t temp = 0x0000;    //中间变量,详情见下
        int length = 0;
        int flag = 10;
    
        odata = fopen(DATA_name, "rb+");
        if (odata == NULL)
        {
            return FAILURE;
        }
    
        long long int t_offset;
        t_offset = i_offset * offset * 2;
        flag = _fseeki64(odata, t_offset, 0);
    
        //bytes_read = fread(obuf, 1, (2 * readSum), odata);
        memset(Iri_data, 0, readSum);    //将Iri_data元素换成0
        //使用fgetc,不需要obuf使用
        for (int i = 0; i < readSum; i++)
        {
            char obuf0 = fgetc(odata);
            char obuf1 = fgetc(odata);
    
            mid_data = obuf0;
            mid_data << 8;
            mid_data &= 0xff00;
            temp = obuf1 & 0x00ff;
            mid_data |= temp;
            Iri_data[i] = mid_data;
    
            length = i + 1;        //读取长度
        }
    
        fclose(odata);
    
        return length;
    }
    
    
  • 相关阅读:
    使用knockout.js 完毕template binding
    站点建设价格为什么有高有低?站点建设该怎样选择?
    同余 模算术 中国剩余定理
    linux 命令之 apt-get
    [Leetcode]-Reverse Integer
    《Python入门》Windows 7下Python Web开发环境搭建笔记
    javascript之Ajax起步
    SQL server 2012 安装SQL2012出现报错: 启用 Windows 功能 NetFx3 时出错
    C# 反射具体解释
    java8_api_jni
  • 原文地址:https://www.cnblogs.com/SunGuiY/p/13258428.html
Copyright © 2020-2023  润新知