利用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;
}

原文地址:https://www.cnblogs.com/SunGuiY/p/13258428.html