[英]Reading a Binary file in C
当前正在尝试用C编写程序以读取.bin文件。 正如您从我的代码中看到的那样,我显然缺少一些东西,尽管我尝试阅读很多东西,但仍然完全陷入困境。 不出所料,我的输出不是预期的。 我的预期输出示例为YV2840 KCLT KDAB 2014年1月16日星期四12:44:00
当我尝试读取有关航空公司航班的.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;
}
如果要将二进制数据读入程序,则需要查看并查看尝试读取的内容。 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|
根据您的描述,您将获得航班号,出发机场,目的地机场和时间戳。 查看数据,您会发现航班号YV2827
(以零终止),您有KCLT
,这是夏洛特/道格拉斯国际机场的IACO标识符。 机场,下一个KSRQ
(佛罗里达州萨拉索塔的IACO标识符),填充了几个字节,最后是一个4字节的数字,代表时间戳。 因此,数据文件很有意义。
现在怎么读呢? 如果您的描述成立,那么包含元素的结构应提供一种读取数据的方法。 您可能需要使用不同的成员和不同的属性才能使填充生效,但是应该满足以下要求:
typedef struct {
char flight[7];
char dept[5];
char dest[5];
unsigned tstamp;
} flight;
接下来,如何读取文件,并将值存储在代码中的内存中。 如果您不需要存储值,则只需简单读取和打印数据即可。 假设您需要存储它以实际使用数据,然后在不知道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);
}
上面的代码在'flts'中分配了指向飞行的初始指针,并使用静态结构buf
作为缓冲区从acars.bin文件中读取数据。 上,其中一个读出nbytes
被读出并且是非零的,存储器被分配给缓冲器的存储flts[idx]
和memcpy
用于将数据从复制buf
到flts[idx]
(您应该添加验证,以确保读取的内容实际上是您期望的内容)。
使用一种标准的重新分配方案,首先将maxs
指针分配给struct,当达到该数量时,该指针的数量将通过xrealloc_dp
重新分配为当前数量的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));
}
其中flts[i]->tstamp
被flts[i]->tstamp
为time_t
,然后与ctime
一起使用,以提供格式化的日期以及其他航班数据输出。
将所有部分放在一起,并了解xcalloc
和xrealloc_dp
只是calloc
和realloc
简单错误检查宏,您可以使用类似以下的内容。 acars.bin
包含2778
航班,下面的代码仅显示前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;
}
产量
$ ./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
记忆错误/泄漏检查
在您的任何可以动态分配内存的代码中,必须使用内存错误检查程序来确保未超出分配的内存进行写操作,并确认已释放所有已分配的内存。 对于Linux, valgrind
是通常的选择。 滥用内存块的微妙方法有很多,它们可能导致真正的问题,没有任何借口不这样做。 每个平台都有类似的内存检查器。 它们易于使用。 只需通过它运行程序即可。
$ 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)
分配了134,011
字节, 并释放了所有堆块-不可能泄漏,确认您正在释放分配的所有内存。 错误摘要:来自0上下文的0错误确认没有在分配的内存块之外进行无意的写操作。
查看代码,让我知道您是否有任何疑问,我们将竭诚为您服务。
读取二进制文件不是简单的操作,因为它们依赖于编译器,因为它们的结构(用于写入或读取)取决于生成数据或用于读取数据的struct
的布局。
在您的二进制文件中,记录看起来像这样构造:
0x59563238323700 (flight number 7 bytes)
0x4B434C5400 (original airport 5 bytes)
0x4B53525100 (dest airport 5 bytes)
0x000000 (3 bytes padding)
0x2C83D052 (4 bytes timestamp)
如您所见,前三个字段是7 + 5 + 5 = 17字节,但是时间戳的int
数据类型在生成二进制数据的程序中需要4
字节对齐,因此数据以0s填充为20
字节。
这意味着您必须确保struct
的布局与生成该二进制数据的struct
完全相同,或者必须在反转原始数据格式后考虑填充以逐字段读取它。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.