CS50 pset4 recover - 恢复的图像不匹配

CS50 pset4 recover - Recovered image does not match

我已经尝试解决这个问题至少一个星期了,似乎无法理解问题出在哪里,我已经检查了 google 中的所有内容,并且不认识任何真正的程序员生活问他们个人,所以如果有人能帮助我,那就太好了。

None 个图像生成负载,它没有恢复 50 个,它恢复了 986 个。

我在 check50 中得到这个结果:

:) recover.c 存在。

:) recover.c 编译。

:) 处理缺少取证图像

:( 正确恢复 000.jpg

恢复的图像不匹配

:( 正确恢复中间图像

恢复的图像不匹配

:( 正确恢复 049.jpg

恢复的图像不匹配

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

typedef uint8_t BYTE;

#define BLOCK_SIZE 512

int main(int argc, char *argv[])
{
    //it only accepts one comand argument in the name of an image
    if (argc != 2)
    {
        printf("Usage: ./recover IMAGE");
        return 1;
    }

    //check if it can open the image
    FILE *file = fopen(argv[1], "r");

    if (file == NULL)
    {
        printf("The image cannot be opened");
        return 1;
    }

    bool jpg_before = false;
    int counter = 0;
    FILE *image = NULL;
    char name[8];
    unsigned char buffer[BLOCK_SIZE];

    //while there is still jpegs in the file
    while (fread(buffer, BLOCK_SIZE, 1, file) == 1)
    {
        if (buffer[0] == 0xff && buffer[1] == 0xd8 && buffer[2] == 0xff && (buffer[3] & 0xe0) == 0xe0)
        {
            jpg_before = true;
        }

        if(jpg_before == true)
        {
            sprintf(name, "%03i.jpg", counter);
            counter++;
            image = fopen(name, "a");
            fwrite(buffer, BLOCK_SIZE, 1, image);
            fclose(image);
        }

    }
    fclose(file);
}

(还请记住我是编程新手,16 岁,英语不是我的母语)

  1. 当您在输入中检测到 header 时,您设置 jpg_before。但是,你永远不会清除它。
  2. 设置标志后,每个块将放入不同的文件中。
  3. 每个输出文件应该包含一个header,然后是相关的数据块。
  4. name[8] 有点太小了。编译器会抱怨,因为 int 可能 [理论上] 是 10 位左右,所以 sprintf 可能会溢出。不要吝啬——使用(例如):char name[20];
  5. 输出文件应使用 "w" 而不是 "a" 打开。如果程序 运行 两次,第二次,输出文件将不正确。

重构代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
//#include <cs50.h>

typedef uint8_t BYTE;

#define BLOCK_SIZE 512

int
main(int argc, char *argv[])
{
    // it only accepts one comand argument in the name of an image
    if (argc != 2) {
        printf("Usage: ./recover IMAGE");
        return 1;
    }

    // check if it can open the image
    FILE *file = fopen(argv[1], "r");

    if (file == NULL) {
        printf("The image cannot be opened");
        return 1;
    }

    int counter = 0;
    FILE *image = NULL;
    char name[20];
    unsigned char buffer[BLOCK_SIZE];

    // while there is still jpegs in the file
    while (fread(buffer, BLOCK_SIZE, 1, file) == 1) {
        if (buffer[0] == 0xff && buffer[1] == 0xd8 && buffer[2] == 0xff && (buffer[3] & 0xe0) == 0xe0) {
            if (image != NULL)
                fclose(image);

            sprintf(name, "%03i.jpg", counter);
            counter++;

            image = fopen(name, "w");
        }

        fwrite(buffer, BLOCK_SIZE, 1, image);
    }

    if (image != NULL)
        fclose(image);

    fclose(file);
}

更新:

来自以下评论:

Points 2 and 3 look to be handled by the file being opened for appending. Leaving the file open is probably a better idea, though. Faster and handles point 6. – user4581301

如果 H 是 header 并且 D 是数据,对于(例如)的输入:H1,D1,D2,D3,D4,H2,D5,D6,D7:

而不是两个输出文件:F0:H1,D1,D2,D3,D4F1:H2,D5,D6,D7

我们有:F0:H1F1:D1F2:D2F3:D3F4:D4F5:H2F6:D5, F7:D6, F8:D7

虽然我重构的代码是正确的,但我回答的顶部部分对 OP 代码实际执行的操作的分析不正确。

我已经解决了。但是,为了使 user4581301 有意义,这里是原始分析:

  1. 当您在输入中检测到 header 时,您设置 jpg_before。但是,你永远不会清除它。
  2. 写入header块的输出流,因此任何数据都不会复制。因此,每个输出文件 为 512 字节
  3. 您在写入 header 后立即关闭输出流。它应该保持打开状态。
  4. 每个块都必须转到给定的输出文件,而不仅仅是 header。
  5. name[8] 有点太小了。编译器会抱怨,因为 int 可能 [理论上] 是 10 位左右,所以 sprintf 可能会溢出。不要吝啬——使用(例如):char name[20];
  6. 输出文件应使用 "w" 而不是 "a" 打开。如果程序 运行 两次,第二次,输出文件将不正确。

更新#2:

First of all thanks! But it is giving me a segmentation fault, do you have any idea why? because everything seems correct – Isa M

根据代码检查,唯一 可能 段错误的地方是 fwrite 调用(即 imageNULL).

我通过 运行 在 gdb [我有 cs50 恢复输入文件] 下的程序确认了这一点。当程序出错时,只需执行 tb 即可获得堆栈回溯。

image 可能是 NULL,原因如下:

  1. 输出文件的 fopen 可能会失败(由于权限、space 等)和 return NULL。调用后没有检查,因为有打开输入文件。

  2. image 开始是 NULL。如果有某种额外的文件 data/file header before the first jpg header (e.g. before FF/D8/FF/E0) ifnot 匹配 first 块读取。即使 image.

    中有 NULL,fwrite 也会被调用

选项(2)是实际发生的,因为cs50的文件在文件顶部有一个额外的header。您可以通过使用十六进制 editor/dumper(例如)odxxd:

检查文件来看到这一点
00000000: 00000000 00000000 00000000 00000000  ................
*
00000200: 63733530 2E6C792F 73757270 72697365  cs50.ly/surprise
00000210: 00000000 00000000 00000000 00000000  ................
*
00000400: FFD8FFE0 00104A46 49460001 01000001  ......JFIF......

代码将不会看到有效的header(即if匹配)直到偏移量400。所以,有开始时有两个 个无关的 fread 调用,直到事情同步。

解决方法是更改​​:

fwrite(buffer, BLOCK_SIZE, 1, image);

进入:

if (image != NULL)
    fwrite(buffer, BLOCK_SIZE, 1, image);

这个问题我以前写过几个答案。但是,我忘记包括这个。我刚写了代码,但 没有 测试它 ;-)

为了解决问题,我添加了更多 return 代码检查并向 fopen 调用添加了 "rb""wb",以防万一运行在 Windoze 上运行。

这是 updated/fixed 代码(这次我 测试了 ;-):

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
//#include <cs50.h>

typedef uint8_t BYTE;

#define BLOCK_SIZE 512

void
onerr(const char *action,const char *file)
{

    printf("%s -- %s -- %s\n",action,file,strerror(errno));
    exit(1);
}

int
main(int argc, char *argv[])
{
    // it only accepts one comand argument in the name of an image
    if (argc != 2) {
        printf("Usage: ./recover IMAGE");
        return 1;
    }

    // check if it can open the image
    FILE *file = fopen(argv[1], "rb");
    if (file == NULL)
        onerr("The image cannot be opened",argv[1]);

    int counter = 0;
    FILE *image = NULL;
    char name[20];
    unsigned char buffer[BLOCK_SIZE];

    // while there is still jpegs in the file
    while (fread(buffer, BLOCK_SIZE, 1, file) == 1) {
        if (buffer[0] == 0xff &&
            buffer[1] == 0xd8 &&
            buffer[2] == 0xff &&
            (buffer[3] & 0xe0) == 0xe0) {
            if (image != NULL)
                fclose(image);

            sprintf(name, "%03i.jpg", counter);
            counter++;

            image = fopen(name, "wb");
            if (image == NULL)
                onerr("unable to open output file",name);
        }

#if 0
        fwrite(buffer, BLOCK_SIZE, 1, image);
#else
        if (image != NULL)
            fwrite(buffer, BLOCK_SIZE, 1, image);
#endif
    }

    if (image != NULL)
        fclose(image);

    fclose(file);

    return 0;
}