我不明白为什么 fread 不能正确填充我的缓冲区。 CS50回收

I cannot figure out why fread will not populate my buffer properly. CS50 recovery

我已经在 gdb 中检查这段代码好几个小时了。我知道 fread 正在返回适当数量的字节 (512)。甚至检查 $eax 打印以确认。有没有人可以提示我我的逻辑有什么问题?

我认为 headers 可能从文件的开头偏移,所以我想在第一次读取时逐字节查找十六进制匹配并设置 fseek 会做诡计。没有这样的运气。打印匹配结果为 0.

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

typedef uint8_t       BYTE;
typedef enum { false, true } boolean;

int main(int argc, char *argv[])
{
    // get filenames from cml input and open file
    char *infile = argv[optind];
    char *fileName = "image";

    FILE *rawData = fopen(infile, "r");
    FILE *imgJPG  = fopen(fileName, "w");
    int match  = 0;
    int imgCnt = 0;

    // buffer to hold 512 bytes of file data - FAT file system
    BYTE *FATbuffer = (BYTE *)malloc(sizeof(BYTE) * 512);

    if (rawData == NULL)
    {
        printf("Error processing file. Exiting...");
        return 1;
    }

    // begin reading raw data and writing it to buffer
    while (fread(FATbuffer, sizeof(BYTE), 512, rawData) == 512)
    {
        if (imgCnt == 0)
        {
            for (int c = 0; c < 512; c++)
            {
                if (FATbuffer[c + 0] == 0xff &&
                    FATbuffer[c + 1] == 0x8d &&
                    FATbuffer[c + 2] == 0xff)
                {
                    fseek(rawData, c, SEEK_SET);
                    imgCnt++;
                    match++;
                }
            }
        }
        else
        {
            if (FATbuffer[0] == 0xff &&
                FATbuffer[1] == 0x8d &&
                FATbuffer[2] == 0xff &&
                imgCnt > 0)
            {
                sprintf(fileName, "%d.jpg", imgCnt);
                fclose(imgJPG);
                imgCnt++;
            }

            if (imgJPG == NULL)
            {
                printf("Error processing file. Exiting...");
                return 3;
            }

            fwrite(FATbuffer, sizeof(BYTE), 512, imgJPG);
        }
    }

    printf("%d\n", match);
    // file processed, free memory
    free(FATbuffer);
    return 0;
}

您的代码中存在多个问题:

  • 您应该测试命令行参数是否可用(在处理选项之后,您可能删除了用于发布的代码)。

  • 文件必须以二进制模式打开以避免可能的翻译结束。

  • 您应该延迟打开 imgJPG,直到找到 JPG header。

  • 不需要分配FATbuffer,定义一个自动存储的512字节数组就可以了

  • 您一次扫描一个块来寻找 JPG 签名,但如果它跨越 512 字节边界并且您访问超出 FATbuffer 末尾的 2 个字节,您可能会错过签名c 大于 509 时的数组。

  • fseek(rawData, c, SEEK_SET); 将文件位置设置为距数组开头的偏移量,而不是从文件开头的偏移量。

  • sprintf(fileName, "%d.jpg", imgCnt); 尝试覆盖字符串常量。这具有未定义的行为。你可能是这个意思:

      char fileName[64];
      snprintf(fileName, sizeof fileName, "image%d.jpg", imgCnt);
      FILE *imgJPG = fopen(fileName, "wb");
    

这是一个修改版本,可以提取嵌入数据流中任何位置的 JPG 文件:

#include <errno.h>
#include <getopt.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char *argv[])
{
    // get filenames from cml input and open file
    if (optind >= argc)
        return 1;
    char *infile = argv[optind];
    FILE *rawData = fopen(infile, "rb");  // open the disk image
    char fileName[64];
    FILE *imgJPG = NULL;
    int imgCnt = 0;

    // buffer to hold 512 bytes of file data - FAT file system
    // add an extra 2 bytes to match signature across sector boundaries
    uint8_t FATbuffer[514];

    // begin reading raw data into buffer
    int pos = 2;
    while (fread(FATbuffer + 2, 1, 512, rawData) == 512) {
        for (int c = pos; c < 512; c++) {
            if (FATbuffer[c + 0] == 0xff &&
                FATbuffer[c + 1] == 0x8d &&
                FATbuffer[c + 2] == 0xff) {
                // found signature: skip to a new file
                if (imgJPG) {
                    // write the end of the current image
                    fwrite(FATbuffer + pos, c - pos, 1, imgJPG);
                    fclose(imgJPG);
                }
                pos = c;
                imgCnt++;
                snprintf(fileName, sizeof fileName, "image%d.jpg", imgCnt);
                imgJPG = fopen(fileName, "wb");
                if (imgJPG == NULL) {
                    fprintf(stderr, "Cannot create file %s: %s\n",
                            fileName, strerror(errno));
                    return 3;
                }
            }
        }
        if (imgJPG) {
            // write end of block to current image
            fwrite(FATbuffer + pos, 512 - pos, 1, imgJPG);
        }
        // copy the last 2 bytes to test for signature overlapping blocks
        FATbuffer[0] = FATbuffer[512];
        FATbuffer[1] = FATbuffer[513];
        // uncopied file data starts a 0 now.
        pos = 0;
    }
    if (imgJPG) {
        // write last 2 bytes to current image
        fwrite(FATbuffer, 2, 1, imgJPG);
        fclose(imgJPG);
    }
    printf("%d\n", imgCnt != 0);
    printf("%d images extracted\n", imgCnt);
    return 0;
}

如果可以假定签名位于扇区的开头,则可以简化代码:

#include <errno.h>
#include <getopt.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char *argv[])
{
    // get filenames from cml input and open file
    if (optind >= argc)
        return 1;
    char *infile = argv[optind];
    FILE *rawData = fopen(infile, "rb");  // open the disk image
    char fileName[64];
    FILE *imgJPG = NULL;
    int imgCnt = 0;

    // buffer to hold 512 bytes of file data - FAT file system
    uint8_t FATbuffer[512];

    // begin reading raw data into buffer
    while (fread(FATbuffer, 1, 512, rawData) == 512) {
        if (FATbuffer[c + 0] == 0xff &&
            FATbuffer[c + 1] == 0x8d &&
            FATbuffer[c + 2] == 0xff) {
            // found signature: skip to a new file
            if (imgJPG) {
                fclose(imgJPG);
            }
            imgCnt++;
            snprintf(fileName, sizeof fileName, "image%d.jpg", imgCnt);
            imgJPG = fopen(fileName, "wb");
            if (imgJPG == NULL) {
                fprintf(stderr, "Cannot create file %s: %s\n",
                        fileName, strerror(errno));
                return 3;
            }
        }
        if (imgJPG) {
            // write end of block to current image
            fwrite(FATbuffer, 512, 1, imgJPG);
        }
    }
    if (imgJPG) {
        fclose(imgJPG);
    }
    printf("%d\n", imgCnt != 0);
    printf("%d images extracted\n", imgCnt);
    return 0;
}