CS50 Recover (Pset4) - изображения не восстанавливаются

Итак, я пробую упражнение CS50 Recover (где вам нужно искать файлы jpg на карте памяти, и всякий раз, когда вы их найдете, вы открываете новый файл и записываете найденный jpg в новый файл). Мой код компилируется, но когда я запускаю команду check50, я получаю следующие ошибки:

:( recovers 000.jpg correctly
    recovered image does not match
:( recovers middle images correctly
    recovered image does not match
:( recovers 049.jpg correctly
    recovered image does not match

Может ли кто-нибудь помочь мне понять, что я делаю не так? Это мой код:

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

int main(int argc, char *argv[])
{
// Checking if the user entered a correct input:
    if (argc!=2)
    {
        printf("You're not using it correctly!\n");
        return 1;
    }
    
// Opening the file in argv[1]
    FILE *f=fopen(argv[1], "r");

// Validating that there's a file in argv[1]
    if(f==NULL)
    {
        printf("File hasn't found\n");
        return 1;
    }
    
    unsigned char bytes[512];
    int counter=0;
    FILE *img= NULL;
    
    while (fread(bytes, 512, 1, f)==1)
    {   
        if (bytes[0]==0xff && bytes[1]==0xd8 && bytes[2]==0xff && (bytes[3]&0xf0)==0xe0)
        {
            // If it's the first jpg found:
            if (counter==0)
            {
                img=fopen("000.jpg", "w");
            }
            else
            {
                fclose(img);
                char filename[8];
                sprintf(filename,"%03i.jpg", counter);
                img= fopen(filename, "w");
                if (img==NULL)
                {
                    printf("Couldn't open file\n");
                    return 1;
                }
            }
            counter++;
            fwrite(bytes, 512, 1, img);
        }   
    }
    fclose(img);
    fclose(f);
}

person Shahar Kadar    schedule 24.08.2020    source источник
comment
Во-первых, попробуйте открыть файлы (как для чтения, так и для записи) в обычном режиме: добавьте b в строки режима для fopen(), например rb и wb.   -  person MikeCAT    schedule 24.08.2020


Ответы (1)


Ваша основная проблема в том, что вы неправильно обрабатываете многоблочные файлы.

Вы делаете fwrite , только если текущий блок имеет заголовок. В противном случае вы выбрасываете данные.

Это потому, что fwrite находится внутри блока if, который обнаруживает заголовок.

Если вы встретили первый (т.е. любой) заголовок, у вас будет открытый выходной поток. Следовательно, после этого вы должны выполнять fwrite на каждой итерации цикла.


Поскольку вы устанавливаете img на NULL перед входом во внешний цикл, нет необходимости в особом случае 000.jpg

И, если входной файл не имеет заголовок [когда-либо], последний fclose будет segfault, потому что указатель img будет NULL.


Я аннотировал ошибки [с исправлениями]. Я завернул старый / новый код, если #if 0 блокирует:

#if 0
// old/original code
#else
// new/refactored code
#endif

Вот код:

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

int
main(int argc, char *argv[])
{

    // Checking if the user entered a correct input:
    if (argc != 2) {
        printf("You're not using it correctly!\n");
        return 1;
    }

    // Opening the file in argv[1]
    FILE *f = fopen(argv[1], "r");

    // Validating that there's a file in argv[1]
    if (f == NULL) {
        printf("File hasn't found\n");
        return 1;
    }

    unsigned char bytes[512];
    int counter = 0;
    FILE *img = NULL;

    while (fread(bytes, 512, 1, f) == 1) {
        if (bytes[0] == 0xff && bytes[1] == 0xd8 && bytes[2] == 0xff &&
            (bytes[3] & 0xf0) == 0xe0) {
// NOTE/BUG: no need to special case the first file
#if 0
            // If it's the first jpg found:
            if (counter == 0) {
                img = fopen("000.jpg", "w");
            }
            else {
                fclose(img);
#else
            if (img != NULL)
                fclose(img);
#endif
                char filename[8];

                sprintf(filename, "%03i.jpg", counter);
                img = fopen(filename, "w");
                if (img == NULL) {
                    printf("Couldn't open file\n");
                    return 1;
                }
#if 0
            }
#endif
            counter++;

// NOTE/BUG: this is only executed if the current block has a header string
#if 0
            fwrite(bytes, 512, 1, img);
#endif
        }

// NOTE/FIX: this is the correct placement for the write
#if 1
        if (img != NULL)
            fwrite(bytes, 512, 1, img);
#endif
    }

// NOTE/BUG: if the input file had _no_ header, img will be NULL
#if 0
    fclose(img);
#else
    if (img != NULL)
        fclose(img);
#endif
    fclose(f);
}

Вот полностью очищенный и отредактированный код [без #if 0]:

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

int
main(int argc, char *argv[])
{

    // Checking if the user entered a correct input:
    if (argc != 2) {
        printf("You're not using it correctly!\n");
        return 1;
    }

    // Opening the file in argv[1]
    FILE *f = fopen(argv[1], "r");

    // Validating that there's a file in argv[1]
    if (f == NULL) {
        printf("File hasn't found\n");
        return 1;
    }

    unsigned char bytes[512];
    int counter = 0;
    FILE *img = NULL;

    while (fread(bytes, 512, 1, f) == 1) {
        if (bytes[0] == 0xff && bytes[1] == 0xd8 && bytes[2] == 0xff &&
            (bytes[3] & 0xf0) == 0xe0) {
            if (img != NULL)
                fclose(img);
            char filename[8];

            sprintf(filename, "%03i.jpg", counter);
            img = fopen(filename, "w");
            if (img == NULL) {
                printf("Couldn't open file\n");
                return 1;
            }
            counter++;
        }

        if (img != NULL)
            fwrite(bytes, 512, 1, img);
    }

    if (img != NULL)
        fclose(img);
    fclose(f);
}
person Craig Estey    schedule 24.08.2020