CS50恢复问题-恢复的图像不匹配

时间:2020-11-07 04:50:16

标签: c cs50 recover

由check50 v3.1.2生成的cs50 / problems / 2020 / x / recover的结果 :) restore.c存在。
:) restore.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);
}

1 个答案:

答案 0 :(得分:1)

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

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

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

相反,您想检查当前是否打开了输出文件,如果要打开,则要关闭当前文件,然后打开下一个要写入的输出文件。 (建议使用 mode "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个字符,而将其设置为1个字符。

您可以简单地使用#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(),您的程序现在可以正常地从card.raw恢复所有50个jpg文件。

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