簡體   English   中英

C - 二進制讀取,fread 是反轉順序

[英]C - binary reading, fread is inverting the order

fread(cur, 2, 1, 鰭)

我相信當我得到這個答案時我會覺得自己很愚蠢,但是發生了什么?

cur 是一個指向 code_cur 的指針,一個短的(2 個字節),fin 是一個為二進制讀取打開的流。

如果我的文件是00101000 01000000

我最終得到的是

code_cur = 01000000 00101000

這是為什么? 我還沒有舉辦任何比賽,因為問題確實歸結為這種(至少對我而言)意外的行為。

而且,如果這是常態,我怎樣才能獲得預期的效果?

聚苯乙烯

我應該補充一點,為了“查看”字節,我正在打印它們的整數值。

printf("%d\n",code_cur)

我試了幾次,看起來很可靠。

正如其他人指出的那樣,您需要了解更多關於字節序的知識

您不知道,但您的文件(幸運的是)采用網絡字節順序(Big Endian)。 你的機器是小端的,所以需要更正。 無論是否需要,始終建議進行此更正,因為這將保證您的程序在任何地方運行。

做類似這樣的事情:

{
    uint16_t tmp;

    if (1 == fread(&tmp, 2, 1, fin)) { /* Check fread finished well */
        code_cur = ntohs(tmp);
    } else {
        /* Treat error however you see fit */
        perror("Error reading file");
        exit(EXIT_FAILURE); // requires #include <stdlib.h>
    }
}

ntohs()會將您的值從文件順序轉換為您機器的順序,無論是大端還是小端。

這就是 htonl 和 htons(和朋友)存在的原因。 它們不是 C 標准庫的一部分,但幾乎可以在每個進行網絡連接的平台上使用。

“htonl”的意思是“主機到網絡,長”; “htons”的意思是“主機到網絡,簡稱”。 在這種情況下,“長”表示 32 位,“短”表示 16 位(即使平台聲明“長”為 64 位)。 基本上,每當您從“網絡”(或在您的情況下,您正在閱讀的流)中讀取某些內容時,您都會通過“ntoh*”傳遞它。 當你寫出來時,你通過“hton*”傳遞它

你可以用任何你想要的方式排列這些函數名,除了那些愚蠢的(不,沒有 nton,也沒有 stonl)

正如其他人指出的那樣,這是一個字節序問題。

最高有效字節在您的文件和您的機器中有所不同。 您的文件具有大端(MSB 在前),而您的機器是小端(MSB 在后或 LSB 在前)。

要了解發生了什么,讓我們創建一個包含一些二進制數據的文件:

    uint8_t buffer[2] = {0x28, 0x40}; // hexadecimal for 00101000 01000000
    FILE * fp = fopen("file.bin", "wb"); // opens or creates file as binary
    fwrite(buffer, 1, 2, fp); // write two bytes to file
    fclose(fp);

file.bin已創建並保存二進制值 00101000 01000000,讓我們閱讀它:

    uint8_t buffer[2] = {0, 0};
    FILE * fp = fopen("file.bin", "rb");
    fread(buffer, 1, 2, fp); // read two bytes from file
    fclose(fp);
    printf("0x%02x, 0x%02x\n", buffer[0], buffer[1]);
    // The above prints 0x28, 0x40, as expected and in the order we wrote previously

所以一切正常,因為我們正在逐字節讀取並且字節沒有字節序(從技術上講,它們總是最重要的位,無論您的機器如何,但您可能會認為它們好像沒有簡化理解)。

無論如何,正如您所注意到的,當您嘗試直接閱讀短片時會發生以下情況:

    FILE * fp_alt = fopen("file.bin", "rb");
    short incorrect_short = 0;
    fread(&incorrect_short, 1, 2, fp_alt);
    fclose(fp_alt);
    printf("Read short as machine endianess: %hu\n", incorrect_short);
    printf("In hex, that is 0x%04x\n", incorrect_short);
    // We get the incorrect decimal of 16424 and hex of 0x4028!
    // The machine inverted our short because of the way the endianess works internally

最糟糕的是,如果您使用的是大端機器,上述結果不會返回不正確的數字,讓您不知道您的代碼是特定於端的,並且不能在處理器之間移植!

使用arpa/inet.h ntohs來轉換字節序很好,但我覺得很奇怪,因為它是一個完整的(非標准)庫,用於網絡通信以解決來自讀取文件的問題,它通過以下方式解決從文件中錯誤地讀取它,然后“翻譯”不正確的值,而不是正確地讀取它。

在高級語言中,我們經常看到處理從文件中讀取字節序而不是轉換值的函數,因為我們(通常)知道文件結構及其字節序,只需查看 Javascript Buffer 的readInt16BE 方法,直截了當且易於使用。

受這種簡單性的啟發,我創建了一個函數,它讀取下面的 16 位整數(但如果需要,可以很容易地更改為 8、32 或 64 位):

#include <stdint.h> // necessary for specific int types

// Advances and reads a single signed 16-bit integer from the file descriptor as Big Endian
// Writes the value to 'result' pointer
// Returns 1 if succeeds or 0 if it fails
int8_t freadInt16BE(int16_t * result, FILE * f) {
    uint8_t buffer[sizeof(int16_t)];
    if (!result || !f || sizeof(int16_t) != fread((void *) buffer, 1, sizeof(int16_t), f))
        return 0;
    *result = buffer[0] << 8 + buffer[1];
    return 1;
}

用法很簡單(為簡潔起見省略了錯誤處理):

    FILE * fp = fopen("file.bin", "rb"); // Open file as binary
    short code_cur = 0;
    freadInt16BE(&code_cur, fp);
    fclose(fp);
    printf("Read Big-Endian (MSB first) short: %hu\n", code_cur);
    printf("In hex, that is 0x%04x\n", code_cur);
    // The above code prints 0x2840 correctly (decimal: 10304)

如果文件不存在、無法打開或不包含在當前位置讀取的 2 個字節,則該函數將失敗(返回 0)。

作為獎勵,如果您碰巧找到小端格式的文件,則可以使用此函數:

// Advances and reads a single signed 16-bit integer from the file descriptor as Little Endian
// Writes the value to 'result' pointer
// Returns 1 if succeeds or 0 if it fails
int8_t freadInt16LE(int16_t * result, FILE * f) {
    uint8_t buffer[sizeof(int16_t)];
    if (!result || !f || sizeof(int16_t) != fread((void *) buffer, 1, sizeof(int16_t), f))
        return 0;
    *result = buffer[1] << 8 + buffer[0];
    return 1;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM