2015-11-15 222 views
0

當前嘗試在C中編寫程序以讀取.bin文件。正如你可以通過我的代碼看到的,我明顯錯過了一些東西,我試圖閱讀很多東西,但仍然完全停滯不前。正如預期的那樣,我的輸出不是打算的。我的預期輸出示例是 YV2840 KCLT KDAB Thu Jan 16 12:44:00 2014讀取C中的二進制文件

正如我正在閱讀關於航空公司航班的.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; 
} 
+0

你需要做的第一件事是檢查來自fopen和fread的返回值。您的問題可能與未成功打開文件一樣簡單。 –

+0

每個記錄都是24個字節,你的結構是42或74,具體取決於你的系統上的int大小(我們不知道這個大小,你應該使用像int32_t這樣的顯式類型或其他合適的),所以你有個問題。 'int FlightNum [7]'是絕對錯誤的,因爲航班號是字符。 – hobbs

+0

使48或80,我忘了填補之前的最後一場。 – hobbs

回答

1

讀二進制文件不是一個簡單的操作,因爲它們依賴於編譯器在某種意義上說,它們的結構,用於寫入或讀取,取決於生成數據或用於讀取它的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字節在生成該二進制數據的程序中進行對齊,所以數據填充爲20字節,其中0。

這意味着您必須確保您的struct的佈局與生成該二進制數據的佈局完全相同,或者在反轉原始數據格式之後考慮填充,逐字段地讀取它。

1

如果您要將二進制數據讀入您的程序,那麼您需要查看並查看您正在嘗試讀取的內容。 hexdumpod是偉大的工具,用於查看數據:

$ 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,這是Charlotte/Douglass Intl的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重新分配給兩次電流量(其爲雙指針宏簡單再分配 - 你也可以使用一個簡單的函數)這裏的目的只是爲了保持代碼的主體乾淨,所以邏輯不會被所有realloc驗證碼等遮擋。

繼完整閱讀acars.bin,然後您將所有值存儲在flts(請注意,時間戳存儲爲unsigned int值,因此轉換爲日曆時間類型並格式化輸出留給y我們的輸出例程)。輸出一個簡單的格式化可能是:

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被轉換爲time_t,然後用ctime用於與飛行數據的其餘一起提供輸出格式化的日期。

把所有的拼在一起,並理解xcallocxrealloc_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; 
} 

輸出

$ ./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 

MEMOR錯誤/泄漏檢查

在您的寫入動態分配內存的任何代碼中,您必須使用內存錯誤檢查程序來確保你沒有寫超出你分配的內存,並確認你已經釋放了你分配的所有內存。對於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錯誤確認分配的內存塊之外沒有無意的寫入。

查看代碼,讓我知道如果您有任何問題,我會很樂意進一步幫助。