用C读取二进制文件

Pat*_*els 3 c struct fread

目前正在尝试用 C 编写程序来读取 .bin 文件。正如您在我的代码中看到的那样,我显然遗漏了一些东西,我尝试阅读了很多内容,但仍然完全卡住了。正如预期的那样,我的输出不是预期的。我预期的输出示例是 YV2840 KLT KDAB Thu Jan 16 12:44:00 2014

因为我正在尝试阅读有关航空公司航班的 .bin 文件。我认为它可能是错误的原因如下。

我应该定义一个名为“人类可读的日期字符串”的结构。这当然是不可能的,因为它会产生编译器错误。也许我不应该从字面上理解它,因为现在我将它定义为“时间戳”。

顺序和大小与写入文件的格式不匹配。

这是 bin 文件,如果有人感兴趣:http : //www.filedropper.com/acars 这是我的代码:

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

typedef struct MyStruct_struct {
    int FlightNum[7];
    char OriginAirportCode[5]; 
    char DestAirportCode[5];
    int TimeStamp;
} MyStruct;

int main() {
    FILE * bin;
    MyStruct myStruct;
    bin = fopen("acars.bin", "rb");

    while(1) {
        fread(&myStruct,sizeof(MyStruct),1,bin);
        if(feof(bin)!=0)
            break;
        printf("%d",myStruct.FlightNum);
        printf("%s" ,myStruct.OriginAirportCode);
        printf("%s" ,myStruct.DestAirportCode);
        printf("%d", myStruct.TimeStamp);
    }

    fclose(bin);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

Dav*_*ica 5

如果您要将二进制数据读入您的程序,那么您需要查看并查看您正在尝试读取的内容。hexdump或者od是查看数据的好工具:

$ hexdump -C -n 512 dat/acars.bin
00000000  59 56 32 38 32 37 00 4b  43 4c 54 00 4b 53 52 51  |YV2827.KCLT.KSRQ|
00000010  00 00 00 00 2c 83 d0 52  59 56 32 37 38 32 00 4b  |....,..RYV2782.K|
00000020  43 4c 54 00 4b 53 52 51  00 00 00 00 cc 3e ed 52  |CLT.KSRQ.....>.R|
00000030  59 56 32 37 33 32 00 4b  43 4c 54 00 4b 53 52 51  |YV2732.KCLT.KSRQ|
00000040  00 00 00 00 88 f4 d5 52  59 56 32 36 37 35 00 4b  |.......RYV2675.K|
00000050  43 4c 54 00 4b 53 52 51  00 00 00 00 20 57 9f 52  |CLT.KSRQ.... W.R|
00000060  59 34 39 38 34 31 00 4b  4d 43 4f 00 4d 4d 4d 58  |Y49841.KMCO.MMMX|
Run Code Online (Sandbox Code Playgroud)

根据您的描述,您有航班号、出发机场、目的地机场和时间戳。查看数据,您会发现一个航班号YV2827(以空字符结尾),KCLT这是 Charlotte/Douglass Intl 的 IACO 标识符。机场,接下来KSRQ(佛罗里达州萨拉索塔机场的 IACO 标识符),几个字节的填充,最后是代表时间戳的 4 字节数字。所以数据文件是有道理的。

现在怎么读?如果您的描述成立,那么包含元素的结构应该提供一种读取数据的方法。您可能需要使用不同的成员和不同的属性来解决填充问题,但接近以下内容的方法应该可行:

typedef struct {
    char flight[7];
    char dept[5];
    char dest[5];
    unsigned tstamp;
} flight;
Run Code Online (Sandbox Code Playgroud)

接下来,如何读取文件,并将值存储在代码中的内存中。如果您不需要存储这些值,那么您只需要简单地读取和打印数据即可。假设您需要存储它以对数据进行一些实际使用,那么在不知道 中包含多少航班的情况下acars.bin,您将需要一个方案来读取/分配内存来保存数据。

一种灵活的方法是使用静态缓冲区将每个航班读入,然后使用malloc/calloc分配一个指向航班的指针数组,并realloc根据需要保存航班数据。就像是:

    flight buf = {{0}, {0}, {0}, 0};
    flight **flts = NULL;
    size_t idx = 0;
    size_t nbytes = 0;
    ...
    /* allocate MAXS pointers to flight */
    flts = xcalloc (MAXS, sizeof *flts);

    /* read into buf until no data read, allocate/copy to flts[i] */
    while ((nbytes = fread (&buf, sizeof buf, 1, fp))) {
        flts[idx] = calloc (1, sizeof **flts);
        memcpy (flts[idx++], &buf, sizeof **flts);

        if (idx == maxs)  /* if pointer limit reached, realloc */
            flts = (flight **)xrealloc_dp((void *)flts, &maxs);
    }
Run Code Online (Sandbox Code Playgroud)

上面的代码在 'flts' 中分配了初始数量的 Flight 指针,并使用静态结构buf作为缓冲区从 acars.bin 文件读取数据。在nbytes读取且非零的读取中,内存被分配用于存储缓冲区,flts[idx]memcpy用于将数据从 复制bufflts[idx]。(您应该验证所读取的内容实际上是您所期望的)。

使用标准的重新分配方案,首先分配maxs指向结构的指针,当达到该数量时,指针数量重新分配为当前数量的两倍xrealloc_dp(这是双指针宏的简单重新分配 - 您可以使用也是简单的函数)这里的目的只是为了保持代码主体干净,这样逻辑就不会被所有realloc验证代码等掩盖。

在完整读取 acars.bin 之后,您将所有值存储在其中flts(注意时间戳存储为一个unsigned int值,因此转换为日历时间类型并格式化输出留给您的输出例程)。输出的简单重新格式化可能是:

    for (i = 0; i < 10; i++) {
        time_t fdate = (time_t)flts[i]->tstamp;
        printf (" flight[%4zu]  %-8s  %-5s  %-5s  %s", i, flts[i]->flight,
                flts[i]->dept, flts[i]->dest, ctime (&fdate));
    }
Run Code Online (Sandbox Code Playgroud)

whereflts[i]->tstamp被转换为time_t然后使用 withctime来提供一个格式化的日期和其余的航班数据一起输出。

将所有部分放在一起,并理解xcallocandxrealloc_dp只是简单的错误检查宏callocrealloc,您可以使用如下所示的内容。包含2778航班,acars.bin下面的代码只是打印前 10 次和最后 10 次航班的数据:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

/* calloc with error check - exits on any allocation error */
#define xcalloc(nmemb, size)       \
({  void *memptr = calloc((size_t)nmemb, (size_t)size);    \
    if (!memptr) {          \
        fprintf(stderr, "error: virtual memory exhausted.\n");  \
        exit(EXIT_FAILURE); \
    }       \
    memptr; \
})

/* realloc with error check - exits on any allocation error */
#define xrealloc_dp(ptr,nmemb)   \
({ \
    void **p = ptr; \
    size_t *n = nmemb;  \
    void *tmp = realloc (p, 2 * *n * sizeof tmp);       \
    if (!tmp) { \
        fprintf (stderr, "%s() error: virtual memory exhausted.\n", __func__);  \
        exit (EXIT_FAILURE);    \
    }   \
    p = tmp;    \
    memset (p + *n, 0, *n * sizeof tmp); /* set new pointers NULL */    \
    *n *= 2;    \
    p;  \
})

#define MAXS 256

typedef struct {
    char flight[7];
    char dept[5];
    char dest[5];
    unsigned tstamp;
} flight;

int main (int argc, char **argv) {

    flight buf = {{0}, {0}, {0}, 0};
    flight **flts = NULL;
    size_t idx = 0;
    size_t nbytes = 0;
    size_t maxs = MAXS;
    size_t i, index;
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;

    if (!fp) {
        fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
        return 1;
    }

    /* allocate MAXS pointers to flight */
    flts = xcalloc (MAXS, sizeof *flts);

    /* read into buf until no data read, allocate/copy to flts[i] */
    while ((nbytes = fread (&buf, sizeof buf, 1, fp))) {
        flts[idx] = calloc (1, sizeof **flts);
        memcpy (flts[idx++], &buf, sizeof **flts);

        if (idx == maxs)  /* if pointer limit reached, realloc */
            flts = (flight **)xrealloc_dp((void *)flts, &maxs);
    }
    if (fp != stdin) fclose (fp);

    printf ("\n There are '%zu' flights in acars data.\n", idx);

    printf ("\n The first 10 flights are:\n\n");
    for (i = 0; i < 10; i++) {
        time_t fdate = (time_t)flts[i]->tstamp;
        printf (" flight[%4zu]  %-8s  %-5s  %-5s  %s", i, flts[i]->flight,
                flts[i]->dept, flts[i]->dest, ctime (&fdate));
    }

    printf ("\n The last 10 flights are:\n\n");
    index = idx - 10;
    for (i = index; i < idx; i++) {
        time_t fdate = (time_t)flts[i]->tstamp;
        printf (" flight[%4zu]  %-8s  %-5s  %-5s  %s", i, flts[i]->flight,
                flts[i]->dept, flts[i]->dest, ctime (&fdate));
    }

    /* free memory */
    for (i = 0; i < idx; i++)
        free (flts[i]);
    free (flts);

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

输出

$ ./bin/readacars dat/acars.bin

 There are '2778' flights in acars data.

 The first 10 flights are:

 flight[   0]  YV2827    KCLT   KSRQ   Fri Jan 10 17:33:00 2014
 flight[   1]  YV2782    KCLT   KSRQ   Sat Feb  1 12:37:00 2014
 flight[   2]  YV2732    KCLT   KSRQ   Tue Jan 14 20:38:00 2014
 flight[   3]  YV2675    KCLT   KSRQ   Wed Dec  4 10:24:00 2013
 flight[   4]  Y49841    KMCO   MMMX   Tue Jul 23 13:25:00 2013
 flight[   5]  Y45981    KMCO   MMMX   Wed Feb 26 13:31:00 2014
 flight[   6]  Y45980    MMMX   KMCO   Tue Mar 25 13:49:00 2014
 flight[   7]  Y40981    KMCO   MMMX   Wed Mar  5 13:23:00 2014
 flight[   8]  Y40980    MMMX   KMCO   Sat Mar 29 11:38:00 2014
 flight[   9]  XX0671    KJFK   MSLP   Tue Mar 25 05:46:00 2014

 The last 10 flights are:

 flight[2768]  4O2993    KJFK   MMMX   Wed Feb 12 09:25:00 2014
 flight[2769]  1L9221    KSAT   KSFB   Thu Jan  9 15:41:00 2014
 flight[2770]  1L1761    KCID   KSFB   Tue Jan 14 13:11:00 2014
 flight[2771]  1L1625    KABE   KSFB   Thu Jan 16 10:22:00 2014
 flight[2772]  1L0751    KMFE   KSFB   Thu Jan 16 19:52:00 2014
 flight[2773]  1L0697    KTYS   KSFB   Wed Jan 15 10:21:00 2014
 flight[2774]  1L0696    KSFB   KTYS   Wed Jan 15 07:00:00 2014
 flight[2775]  1L0655    KIAG   KSFB   Fri Jan 17 21:11:00 2014
 flight[2776]  1L0654    KSFB   KIAG   Fri Jan 17 15:49:00 2014
 flight[2777]  1L0641    KGFK   KSFB   Fri Jan 17 14:21:00 2014
Run Code Online (Sandbox Code Playgroud)

内存错误/泄漏检查

在您编写的任何动态分配内存的代码中,您必须使用内存错误检查程序来确保您没有写入超出分配的内存并确认您已释放所有分配的内存。对于Linuxvalgrind是正常的选择。有很多微妙的方法可以滥用内存块而导致真正的问题,没有理由不这样做。每个平台都有类似的内存检查器。它们易于使用。只需通过它运行您的程序。

$ valgrind ./bin/readacars dat/acars.bin
==12304== Memcheck, a memory error detector
==12304== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==12304== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==12304== Command: ./bin/readacars dat/acars.bin
==12304==

 There are '2778' flights in acars data.

 The first 10 flights are:

 flight[   0]  YV2827    KCLT   KSRQ   Fri Jan 10 17:33:00 2014
 flight[   1]  YV2782    KCLT   KSRQ   Sat Feb  1 12:37:00 2014
 flight[   2]  YV2732    KCLT   KSRQ   Tue Jan 14 20:38:00 2014
<snip>
 flight[2776]  1L0654    KSFB   KIAG   Fri Jan 17 15:49:00 2014
 flight[2777]  1L0641    KGFK   KSFB   Fri Jan 17 14:21:00 2014
==12304==
==12304== HEAP SUMMARY:
==12304==     in use at exit: 0 bytes in 0 blocks
==12304==   total heap usage: 2,812 allocs, 2,812 frees, 134,011 bytes allocated
==12304==
==12304== All heap blocks were freed -- no leaks are possible
==12304==
==12304== For counts of detected and suppressed errors, rerun with: -v
==12304== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)
Run Code Online (Sandbox Code Playgroud)

134,011字节分配和所有堆块都被释放——没有泄漏是可能的确认你正在释放你分配的所有内存。错误摘要:来自 0 个上下文的 0 个错误确认在分配的内存块之外没有意外写入。

查看代码,如果您有任何问题,请告诉我,我很乐意为您提供进一步的帮助。