【问题标题】:Reading in binary file and using data correctly读取二进制文件并正确使用数据
【发布时间】:2014-03-29 07:49:16
【问题描述】:

我正在尝试编写一个 C 程序来显示二进制文件的内容,该文件包含如下存储的几个数字: 前 4 个字节包含一个整数 n,以小端格式存储。 接下来的 8n 字节包含以 8 字节双精度格式存储的 n 个浮点数数组

“数据”是用matlab制作的,这个文件包括:n = 7 array = [.7147, -.2050, -.1241, 1.4897, 1.4090, 1.4172, .6715]

到目前为止,这是我拥有的代码。本质上,我试图读取文件,通过我的小端函数发送这些变量,然后显示它。我不确定为什么我会得到我得到的结果。

#include <stdio.h>
#include <stdint.h>

/* function to show bytes in memory, from location str to str+n*/
void extract_little(char *str,int offs, int n); 
void extract_big(char *str,int offs, int n);
/*Main function to call above function */
int ofset;

int main(){


int x;
int i[numE*4];
FILE* fp = fopen("data","rb");

fread(i,sizeof(i[x]),numE,fp);
fclose(fp);
for (x=1; x < numE;x++)
printf("%d%\n",i[x]);

//extract_little((char *)&i, ofset, sizeof(i));

  return 0;

}


  void extract_little(char *str,int offs, int n) 
    {
    int i;
    for (i = 0; i < n; i++){
       printf(" %04f\n\n", (unsigned int) str[i]);
      }

    }

新成果

-2103482977
1072095020
-359136765
-1077265325
-1953332745
-1077950484

不是 100% 确定这是否正确,但这是一个开始。不幸的是,没有使用它需要的功能。

【问题讨论】:

  • 这在 multiple 级别上是错误的。将double 读取为二进制文件,传递int 的大小作为该列表的前导。将 seven int 值读入 single double 的地址:完全未定义的行为。
  • 对不起,我发布了错误的版本。我已经玩了一段时间了,所以事情变得混乱了。我只是用细微的变化来修复它。我现在在 printf 中是 int 和 unsigned int。但是,我仍然收到一个错误,我无法弄清楚如何修复。
  • 即使在“修复”之后,将 int 大小的 7 个 项读取到 single int 的地址中也会调用 undefined行为。而extract_little 仍然是错误的。支持将单个 char 转换为 unsigned int,但不支持将其打印为 float。您需要查看有关数据提取的说明。您应该读取四个unsigned char,然后使用按位操作从这四个字节中制造一个平台-int 以获取n,然后为ndouble 元素分配空间并将其余数据读入说分配的缓冲区。
  • 好的,我进行了彻底的搜索并打印,完全没有使用我的功能。我(认为)这是正确的,实际上并不确定,因为数字太奇怪了。

标签: c binaryfiles endianness


【解决方案1】:

需要创建一个双精度数组。由于大小记录在文件中,事先不知道,所以我为数组动态分配内存。

如果机器也是 little-endian,则无需转换,但如果是 big-endian,则字节必须反转。要检查机器是否为大端,可以检查已知数字的第一个字节。 int 和 double 在同一个系统上确实有不同的字节顺序是可能的(尽管可能性很小),所以应该检查两者。

#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>

// For detecting endianess.
// dummy_int: highest order byte is zero, lowest order byte is non-zero.
// dummy_double: highest order byte (part of exponent) is non-zero,
// lowest order byte (part of mantissa) is zero.
int32_t dummy_int = 1;
double dummy_double = 1.0;
#define INT_IS_BIG_ENDIAN (((char*) &dummy_int)[0] == 0)
#define DOUBLE_IS_BIG_ENDIAN (((char*) &dummy_double)[0] != 0)

void extract(FILE* fp, void *data, size_t size, int reverse);
void reverse_bytes(void *data, size_t size);
void display(void *data, size_t size);

int main()
{
    int32_t n, i;
    double *data;
    FILE* fp = fopen("data","rb");

    extract(fp, &n, sizeof(n), INT_IS_BIG_ENDIAN);
    data = (double*) malloc(n * sizeof(double));
    for (i = 0; i < n; i++) {
        extract(fp, &data[i], sizeof(double), DOUBLE_IS_BIG_ENDIAN);
    }

    fclose(fp);

    // print the data
    printf("n: %d\n", n);
    for (i = 0; i < n; i++) {
        printf("item #%d: %f\n", i+1, data[i]);
        printf("in hex: ");
        display(&data[i], sizeof(double));
    }

    free(data);

    return 0;
}

void extract(FILE* fp, void *data, size_t size, int reverse)
{
    fread(data, size, 1, fp);
    if (reverse) {
        reverse_bytes(data, size);
    }
}

void reverse_bytes(void *data, size_t size)
{
    char *i, *j;
    char tmp;
    for (i = (char*) data, j = i + size - 1; i < j; i++, j--) {
        tmp = *i;
        *i = *j;
        *j = tmp;
    }
}

void display(void *data, size_t size)
{
    size_t i;
    char *char_data = (char*) data;
    for (i = 0; i < size; i++) {
        printf("%02x ", (unsigned char) char_data[i]);
    }
    printf("\n");
}

【讨论】:

  • 天哪,有很多东西要吸收。非常感谢!我将在我晚上剩下的时间里分析这个。希望我终于可以通过我厚厚的头骨得到这些东西。
  • 你能解释一下这实际上是如何计算出 n 的大小的吗?
  • 变量n 与四个字节的内存相关联。通过将n 的地址传递给fread(),将输入的前四个字节直接读入该内存。在 little-endian 机器上,这就是所有需要做的事情。在大端机器上,四个字节必须颠倒,因为机器希望最低位字节存储在最后一个内存位置而不是第一个内存位置。反转是通过假设n 是一个char 数组并交换相应的元素(第一个与第四个,第二个与第三个)来完成的。
猜你喜欢
  • 2012-09-05
  • 1970-01-01
  • 2019-09-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-02-05
  • 2017-10-18
相关资源
最近更新 更多