使用 C 从 .raw 文件恢复 JPEG 图像

Recovering JPEG image from .raw file using C

我正在做 Harvard's online lecture 提供的习题集。 我终于找到了从文件中恢复一组 JPEG 图像的解决方案 (card.raw)。

似乎代码本身并没有抛出错误,但它返回的是扭曲的图像,我有点不知道为什么会这样。

[Link 以图像为例] https://prnt.sc/q0tb4f

这是我的代码

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


int main(int argc, char *argv[])
{
    //check usage
    if (argc != 2)
    {
        return 1;
    }
    // open file
    FILE* input = fopen(argv[1], "r");
    // return error if file does not existS
    if (!input)
    {
        return 1;
        printf("file does not exists");
    }
    // create an array with 512 bytess of bytes
    unsigned char bytes[512];
    // create count variable
    int count = 0;
    // create an empty string for filename
    char filename[7];
    // create an empty output file
    FILE* output = NULL;
    // repeat until end of input
    while (fread(bytes, 1, sizeof(bytes), input) != 0)
    {
        // read 1 block of 512 bytes at a time
        fread(bytes, 1, sizeof(bytes), input);

        // check if beginning of jpeg file
        if (bytes[0] == 0xff && bytes[1] == 0xd8 && bytes[2] == 0xff && (bytes[3] & 0xf0) == 0xe0)
        {
            // if already found a jpeg, close the file
            if (count > 0)
            {
                fclose(output);
            }
            // name file
            sprintf(filename, "%03i.jpg", count);
            // open file
            output = fopen(filename, "w");
            // write file
            fwrite(bytes, 1, sizeof(bytes), output);
            // increment count
            count++;
        }
        if (output != NULL)
        {
            // keep writing if jpeg header is already found
            fwrite(bytes, 1, sizeof(bytes), output);
        }
    }
     fclose(output);
     fclose(input);
}

我没有受过教育的假设无法理解为什么会发生这种情况。 我只能想象这可能是由于在不正确的步骤中打开和关闭文件而发生的。

这是问题所在:

while (fread(bytes, 1, sizeof(bytes), input) != 0)
{
    // read 1 block of 512 bytes at a time
    fread(bytes, 1, sizeof(bytes), input);

您每个循环调用 fread 两次。结果,循环体只能看到奇数块。删除第二个 fread.

第二个问题(如 @SteveFriedl 指出的那样)是代码用于文件名的缓冲区太小。

char filename[7];
sprintf(filename, "%03i.jpg", count);

像“123.jpg”这样的文件名至少需要 8 个字节,因为您需要为 NUL 终止符留出空间。但是,请注意 "%03i" 使用 至少 3 个字符。它可以使用更多,例如如果 count 达到 1000。所以我将缓冲区声明为 char filename[32]; 以避免缓冲区溢出的任何机会。

当只需要一个时,您还有两个 fwrites

        output = fopen(filename, "w");
        // write file
        fwrite(bytes, 1, sizeof(bytes), output);
        // increment count
        count++;
    }
    if (output != NULL)
    {
        // keep writing if jpeg header is already found
        fwrite(bytes, 1, sizeof(bytes), output);
    }

打开一个新文件后,代码写入第一个块,然后再次写入。删除第一个 fwrite 并让第二个 fwrite 处理第一个块。

另一个问题是代码隐式假设如果 fread 不是 return 0,那么它会读取一个完整的块。如果文件大小是块大小的倍数,那么该假设是可以的,但最好不要做任何假设。所以while循环中的条件应该是

while (fread(bytes, 1, sizeof(bytes), input) == sizeof(bytes))

这里正在恢复工作。

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

int isAJpg(unsigned char bytes[]);

int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        return 1;
    }
    FILE *file = fopen(argv[1], "r");
    if (file == NULL)
    {
        return 1;
    }
   
    char filename[10];
    int count = 0;
    unsigned char bytes[512];
    FILE *output;
    int jpgfound = 0;
   
    while (fread(bytes, 512, 1, file) != 0)
    {
       
        // if it is a jpg
        if (isAJpg(bytes) == 1)
        {
            if (jpgfound == 1)
            {
                fclose(output);
            }
            else
            {
                jpgfound = 1;
            }
            // name file
            sprintf(filename, "%03d.jpg", count);
           
            // open file
            output = fopen(filename, "a");
           
            count++;
        }
        if (jpgfound == 1)
        {
            // writes to a file
            fwrite(&bytes, 512, 1, output);
        }
    }
    //close the files
    fclose(output);
    fclose(file);
}

// check in it is a jpg
int isAJpg(unsigned char bytes[])
{
    if (bytes[0] == 0xff && bytes[1] == 0xd8 && bytes[2] == 0xff && (bytes[3] & 0xf0) == 0xe0)
    {
        return 1;
    }
    return 0;
}