首页 > 解决方案 > CS50 恢复问题 - 恢复的图像不匹配

问题描述

由 check50 v3.1.2 生成的 cs50/problems/2020/x/recover 的结果 :) recover.c 存在。
:) recover.c 编译。
:) 处理缺少取证图像
:( 正确恢复 000.jpg 恢复图像不匹配
:( 正确恢复中间图像 恢复图像不匹配
:( 恢复 049.jpg 正确恢复图像不匹配

我不知道为什么我会面临这个问题。请帮助我,这可能是因为我不清楚文件基础或其他内容。

#include <stdio.h>
#include <stdint.h>

typedef uint8_t BYTE;

int main(int argc, char *argv[])
{
    //to check for command-line arguments
    if( argc !=2 )
    {
        printf("Usage: ./recover imagename");
        return 1;
    }
    
    //file pointer from where to read
    FILE *inptr = fopen(argv[1], "r");

    if( inptr == NULL)
    {
        fprintf(stderr, "File couldn't open");
        return 1;
    }

    BYTE buffer[512]; //buffer for storing input data from file
    char FILENAME[8]; //output file name storage
    int counter=0; //to handle naming of file
    FILE *outptr = NULL; //file pointer where to write

    while(fread(buffer, 512, 1, inptr))
    {
        if(buffer[0] == 0xff && buffer[1] == 0xd8 && buffer[2] == 0xff && (buffer[3] & 0xf0) == 0xe0)
        {
            sprintf(FILENAME, "%03i.jpg", counter);
            outptr = fopen(FILENAME, "a");
            if(outptr != NULL)
            {
                fclose(outptr);
                counter++;
            }
        }
        if(outptr != NULL)
        {
            fwrite(buffer, 512, 1, outptr);
        }
    }
    fclose(outptr);
    fclose(inptr);
}

标签: ccs50recover

解决方案


您的代码有一个块乱序导致您创建空文件。在您的代码中,您具有以下内容:

        sprintf(FILENAME, "%03i.jpg", counter);
        outptr = fopen(FILENAME, "a");
        if(outptr != NULL)
        {
            fclose(outptr);
            counter++;
        }

在哪里创建新文件名,sprintf()然后用fopen(). 如果打开成功,则在将任何内容写入文件之前立即关闭文件fclose()并递增。counter不是你想要的。

相反,您要检查当前是否打开了输出文件,如果是,则要关闭当前文件,然后打开下一个输出文件进行写入。(建议使用模式 "wb"而不是打开"a")。重新排列,您将拥有:

            if(outptr != NULL)  /* if output file open, close before opening next */
            {
                fclose(outptr);
                counter++;
            }
            sprintf(FILENAME, "%03d.jpg", counter);
            outptr = fopen(FILENAME, "wb");     /* open in write, not append mode */

现在您的文件将保持打开状态,因此您可以将恢复的 jpg 写入文件。

避免硬编码文件名或使用幻数

你的文件名做得很好,但你应该为你的代码中使用的数字声明常量。为什么?当您#define在代码顶部设置一个常量时,您提供了一个方便的位置来调整任何固定值,而不必为了进行更改而选择所有循环限制和函数调用。此外,在为文件名等声明缓冲区时,不要吝啬缓冲区大小...... 最好有一个 1000 个字符太长的缓冲区,而它的一个字符太短。

您可以简单地使用#define来定义一个或多个常量或使用全局enum来实现相同的目的。在这里你可以这样做:

#define BLKSZ  512      /* if you need a constant, #define one (or more) */
#define MAXFN 1024      /*         (don't skimp on buffer size)          */
...

然后你的用途是:

    ...
    BYTE buffer[BLKSZ];     /* don't use MagicNumbers, use a constant */
    char FILENAME[MAXFN];   /* ditto */
    ...
    while (fread(buffer, BLKSZ, 1, inptr))
    {
        if (buffer[0] == 0xff && buffer[1] == 0xd8 && 
            buffer[2] == 0xff && (buffer[3] & 0xf0) == 0xe0)
        {
            if(outptr != NULL)  /* if output file open, close before opening next */
            {
                fclose(outptr);
                counter++;
            }
            sprintf(FILENAME, "%03d.jpg", counter);
            outptr = fopen(FILENAME, "wb");     /* open in write, not append mode */
        }
        if(outptr != NULL)
        {
            fwrite(buffer, BLKSZ, 1, outptr);
        }
    }

注意:你想通过换行来控制长行,以防止不必要的换行,这会使代码难以阅读。最近 Linux 内核样式指南将推荐的每行字符从 80 个字符提高到 100 个字符 - 虽然,在 StackOverflow 上,您会注意到您的行开始需要滚动条超过 90 个字符)

现在,如果有任何变化,您需要更改两个简单的常量。

通过重新排序fclose()调用,您的程序现在可以恢复所有 50 个 jpg 文件card.raw

如果您还有其他问题,请告诉我。


推荐阅读