cs50 pset4 Recover-为什么将整个文件写入内存会导致check50失败?

cs50 pset4 Recover-为什么将整个文件写入内存会导致check50失败?,c,cs50,C,Cs50,我正在研究,我想知道我的方法是否存在根本性的缺陷。演练建议使用fread()以512字节的块遍历文件,查找JPEG头,并在每次找到一个新文件时写入一个新文件-00X.jpg。我尝试用malloc()创建一个任意大的临时缓冲区,使用fread()的返回值来确定文件的大小,并将整个文件写入具有两种数据类型的结构数组中。B表示字节,用于存储文件;而.header表示bool,用于指示每个JPEG头的起始位置 我遇到了两个问题。一是恢复的图像没有通过检查50,二是试图从数组中一次写入多个字节会导致垃圾字

我正在研究,我想知道我的方法是否存在根本性的缺陷。演练建议使用fread()以512字节的块遍历文件,查找JPEG头,并在每次找到一个新文件时写入一个新文件-00X.jpg。我尝试用malloc()创建一个任意大的临时缓冲区,使用fread()的返回值来确定文件的大小,并将整个文件写入具有两种数据类型的结构数组中。B表示字节,用于存储文件;而.header表示bool,用于指示每个JPEG头的起始位置

我遇到了两个问题。一是恢复的图像没有通过检查50,二是试图从数组中一次写入多个字节会导致垃圾字节。以下是我正在做的:

typedef uint8_t BYTE;
typedef struct
{
    BYTE B;
    bool header;
}
images;
这将使用bytes和bools定义数据类型BYTE和my struct

BYTE *tmp_buffer = malloc(4000000 * sizeof(BYTE));
int counter = fread(tmp_buffer, sizeof(BYTE), 4000000, file);
images buffer[counter];
这将使用malloc()创建任意大的缓冲区,使用它和fread的返回值来确定文件的字节大小,然后在内存中创建一个要使用的缓冲区

for (int copy = 0; copy < counter; copy++)
{
    buffer[copy].header = false;
    buffer[copy].B = tmp_buffer[copy];
}
free(tmp_buffer);
fclose(file);
for (int check = 0; check < counter; check++)
{
    if (buffer[check].B == 0xff && buffer[check + 1].B == 0xd8 && buffer[check + 2].B == 0xff)
    {
        buffer[check].header = true;
    }
}

每隔一个位置就有一个额外的字节!我在这里不知所措。在这一点上,更多的是理解这些行为。我相信两者都有一个合乎逻辑的解释,但这让我发疯了!我尝试用一个字节数组代替添加了bool的结构,它也做了同样的事情。

正如上面的注释中所写的那样,尝试使用一个结构,并尝试将每个要写出的jpg存储为一次--这让事情变得比需要的困难得多。正如说明书中所讨论的FAT文件系统(位于图像拍摄的卡上),它将每个文件的块存储在512字节的扇区中。要扫描卡,您只需要一个512字节的缓冲区来处理对其输出文件的读写操作。不需要结构,也不需要动态分配内存

读取的方法是从文件中读取每个512数据块。然后需要检查块的前4个字节是否包含jpg头。要测试的简短函数可以编写为:

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

#define FNLEN 128       /* if you need a constant, #define one (or more) */
#define BLKSZ 512

/* check if first 4-bytes in buf match jpg header */
int chkjpgheader (const unsigned char *buf)
{
    return  buf[0] == 0xff && 
            buf[1] == 0xd8 && 
            buf[2] == 0xff && 
            buf[3] >> 4 == 0xe;
}
注意:
jpgcnt
用作计数器和标志,用于控制jpg文件上的第一次
fclose()
何时发生,以及控制第一次写入第一个文件的何时发生。)

看看回报。了解函数中不同位置返回
jpgcnt
jpgcnt-1
的原因。还要理解为什么在发生写操作后总是检查
fclose()
的返回。在将最终数据刷新到文件并关闭文件时,可能会发生许多错误——最后一次检查最后一次写入时不会捕获到这些错误。所以规则——总是在写入后验证关闭。关闭输入文件时不需要进行检查

这就是你所需要的。在
main()
中,您将打开输入文件,只需将打开的文件流传递给
recoverjpgs()
函数,保存返回,即可知道成功恢复了多少jpg文件。它可以简单到:

int main (int argc, char **argv) {
    
    FILE *fp = NULL;            /* input file stream pointer */
    int jpgcnt = 0;             /* count of jpg files recovered */
    
    if (argc < 2 ) {    /* validate 1 argument given for filename */
        fprintf (stderr, "error: insufficient input,\n"
                         "usage: %s filename\n", argv[0]);
        return 1;
    }
    
    /* open file/validate file open for reading */
    if ((fp = fopen (argv[1], "rb")) == NULL) {
        perror ("fopen-argv[1]");
        return 1;
    }
    
    if ((jpgcnt = recoverjpgs(fp)))
        printf ("recovered %d .jpg files.\n", jpgcnt);
    else
        puts ("no jpg files recovered.");
        
    fclose (fp);
}
(将在当前目录中创建50个文件,
file_0001.jpg
file_0050.jpg
,您可以欣赏jgp文件中显示的气球、鲜花、女孩等。)

仔细检查一下,如果你还有其他问题,请告诉我


编辑关于分配和存储要写入的每个文件的每条注释一次

即使您希望在写入一次文件之前对每个文件进行完全缓冲,使用带有单个
uint8_t
(字节)和
bool
的结构来标记该结构是否为头字节的想法也没有多大意义。为什么?它把写程序弄得一团糟。在写入时,必须检查分配块中的每个结构,该块的大小足以容纳整个
card.raw
文件,以捕获4-struct序列,其中每个结构的
bool
标志都设置为true——实际上是重复读取过程中进行的所有测试,以查找头字节并设置
bool
首先是结构成员
true

如前所述,如果有无数个文件,您可能希望扫描来自
card.raw
的输入流,并将每个jpg的字节保存在缓冲区中,以便在进程继续时将它们写入文件一次(您甚至可以
fork
将写操作转移到一个单独的进程,这样,如果您真的想调整内容,就可以在不等待写操作的情况下继续读取

不管怎样,方法都是一样的。如果您动态地为
buf
分配,您可以用每个jpg文件填充它,当找到下一个头时——将
buf
的当前内容一直写到下一个头的开头(将下一个头读到
buf
的开头)然后重复,直到没有要检查的输入

您将在整个过程中重复使用分配给
buf
的存储空间,并且仅当当前文件需要的存储空间大于当前分配的存储空间时才进行扩展。(因此
buf
最终的大小可以容纳一天结束时发现的最大jpg)。这最大限度地减少了分配,意味着所有50个文件所需的
realloc
s是遇到较大文件时所需的
realloc
s。如果接下来的20个文件都适合当前分配的缓冲区,则无需进行调整,并且您不断地用不同的jpg文件反复填充
buf
从“法医图像”中恢复的内容(听起来很重要)

只添加了一个
bufsz
变量来跟踪
buf
的当前分配大小,以及一个
total
变量来跟踪每个jpg文件中读取的总字节数。除此之外,您只需重新排列文件的位置
for (int i = 1024; i < 115200; i+= 512)
{
    fwrite(&buffer[i].B, sizeof(BYTE), 512, img);
}
ff d8 ff e0 00 10 4a 46 49 46 00 01 01 00 00 01
ff 01 d8 00 ff 00 e0 00 00 00 10 00 4a 00 46 00
#include <stdio.h>
#include <stdlib.h>

#define FNLEN 128       /* if you need a constant, #define one (or more) */
#define BLKSZ 512

/* check if first 4-bytes in buf match jpg header */
int chkjpgheader (const unsigned char *buf)
{
    return  buf[0] == 0xff && 
            buf[1] == 0xd8 && 
            buf[2] == 0xff && 
            buf[3] >> 4 == 0xe;
}
/* find each jpg header and write contents to separate file_000x.jpg files.
 * returns the number of jpg files successfully recovered.
 */
int recoverjpgs (FILE *ifp)
{
    char jpgname[FNLEN] = "";       /* jpg output filename */
    unsigned char buf[BLKSZ];       /* read buffer */
    int jpgcnt = 0;                 /* found jpg header count*/
    size_t nbytes;                  /* no. of bytes read/written */
    FILE *fp = NULL;                /* FILE* pointer for jpg output */
    
    /* read until jpg header found */
    while ((nbytes = fread (buf, 1, BLKSZ, ifp)) > 0) {
        /* check if jpg header found */
        if (nbytes >= 4 && chkjpgheader(buf)) {
            /* if not 1st header, close current file */
            if (jpgcnt) {
                if (fclose (fp) == EOF) {   /* validate every close-after-write */
                    perror ("recoverjpg()-fclose");
                    return jpgcnt - 1;
                }
            }
            /* create output filename (e.g. file_0001.jpg) */
            sprintf (jpgname, "file_%04d.jpg", jpgcnt + 1);
            /* open next file/validate file open for writing */
            if ((fp = fopen (jpgname, "wb")) == NULL) {
                perror ("fopen-outfile");
                return jpgcnt;
            }
            jpgcnt += 1;    /* increment recovered jpg count */
        }
        /* if header found - write block in buf to output file */
        if (jpgcnt && fwrite (buf, 1, nbytes, fp) != nbytes) {
            perror ("recoverjpg()-fwrite");
            return jpgcnt - 1;
        }
    }
    /* if file opened, close final file */
    if (jpgcnt && fclose (fp) == EOF) {     /* validate every close-after-write */
        perror ("recoverjpg()-fclose");
        return jpgcnt - 1;
    }
    
    return jpgcnt;  /* return number of jpg files recovered */
}
int main (int argc, char **argv) {
    
    FILE *fp = NULL;            /* input file stream pointer */
    int jpgcnt = 0;             /* count of jpg files recovered */
    
    if (argc < 2 ) {    /* validate 1 argument given for filename */
        fprintf (stderr, "error: insufficient input,\n"
                         "usage: %s filename\n", argv[0]);
        return 1;
    }
    
    /* open file/validate file open for reading */
    if ((fp = fopen (argv[1], "rb")) == NULL) {
        perror ("fopen-argv[1]");
        return 1;
    }
    
    if ((jpgcnt = recoverjpgs(fp)))
        printf ("recovered %d .jpg files.\n", jpgcnt);
    else
        puts ("no jpg files recovered.");
        
    fclose (fp);
}
$ ./bin/recover ~/doc/c/cs50/recover/card.raw
recovered 50 .jpg files.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>

#define FNLEN 128       /* if you need a constant, #define one (or more) */
#define BLKSZ 512
#define JPGSZ 1<<15     /* 32K initial allocation size */

/* write 'nbytes' from 'buf' to 'fname'. returns number of bytes
 * written on success, zero otherwise.
 */ 
size_t writebuf2file (const char *fname, void *buf, size_t nbytes)
{
    FILE *fp = NULL;    /* FILE* pointer for jpg output */
    
    /* open file/validate file open for writing */
    if ((fp = fopen (fname, "wb")) == NULL) {
        perror ("writebuf2file-fopen");
        return 0;
    }
    /* write buffer to file/validate bytes written */
    if (fwrite (buf, 1, nbytes, fp) != nbytes) {
        perror ("writebuf2file()-fwrite");
        return 0;
    }
    /* close file/validate every close-after-write */
    if (fclose (fp) == EOF) {
        perror ("writebuf2file-fclose");
        return 0;
    }
    
    return nbytes;
}

/* check if first 4-bytes in buf match jpg header */
int chkjpgheader (const unsigned char *buf)
{
    return  buf[0] == 0xff && 
            buf[1] == 0xd8 && 
            buf[2] == 0xff && 
            buf[3] >> 4 == 0xe;
}

/* find each jpg header and write contents to separate file_000x.jpg files.
 * returns the number of jpg files successfully recovered.
 */
int recoverjpgs (FILE *ifp)
{
    char jpgname[FNLEN] = "";                   /* jpg output filename */
    int jpgcnt = 0;                             /* found jpg header count*/
    size_t  nbytes,                             /* no. of bytes read/written */
            bufsz = JPGSZ,                      /* tracks current allocation of buf */
            total = 0;                          /* tracks total bytes in jpg file */
    uint8_t *buf = malloc (JPGSZ);              /* read buffer */
    
    if (!buf) { /* validate every allocation/reallocation */
        perror ("malloc-buf");
        return 0;
    }
    
    /* read until jpg header found */
    while ((nbytes = fread (buf + total, 1, BLKSZ, ifp)) > 0) {
        /* check if jpg header found */
        if (nbytes >= 4 && chkjpgheader(buf + total)) {
            /* if not 1st header, write buffer to file, reset for next file */
            if (jpgcnt) {
                /* create output filename (e.g. file_0001.jpg) */
                sprintf (jpgname, "file_%04d.jpg", jpgcnt);
                /* write current buf to file */
                if (!writebuf2file (jpgname, buf, total))
                    return jpgcnt - 1;
                /* move header block to start of buf */
                memmove (buf, buf + total, BLKSZ);
                total = 0;                  /* reset total for next file */
            }
            jpgcnt += 1;    /* increment recovered jpg count */
        }
        /* if header found - began accumulating blocks in buf */
        if (jpgcnt)
            total += nbytes;
        /* check if reallocation required before next read */
        if (total + BLKSZ > bufsz) {
            /* add a fixed 32K each time reallocaiton required
             * always realloc to a temporary pointer to prevent memory leak
             * on realloc failure.
             */
            void *tmp = realloc (buf, bufsz + (1 << 15));
            if (!tmp) {                     /* validate every reallocations */
                perror ("realloc-buf");
                return jpgcnt - 1;
            }
            buf = tmp;              /* assign reallocated block to buf */
            bufsz += 1 << 15;       /* update bufsz with new allocation size */
        }
    }
    /* write final buffer to file */
    if (jpgcnt) {
        /* create output filename (e.g. file_0001.jpg) */
        sprintf (jpgname, "file_%04d.jpg", jpgcnt);
        /* write current buf to file */
        if (!writebuf2file (jpgname, buf, total))
            return jpgcnt - 1;
    }
    
    free (buf);     /* free allocated memory */
    
    return jpgcnt;  /* return number of jpg files recovered */
}

int main (int argc, char **argv) {
    
    FILE *fp = NULL;            /* input file stream pointer */
    int jpgcnt = 0;             /* count of jpg files recovered */
    
    if (argc < 2 ) {    /* validate 1 argument given for filename */
        fprintf (stderr, "error: insufficient input,\n"
                         "usage: %s filename\n", argv[0]);
        return 1;
    }
    
    /* open file/validate file open for reading */
    if ((fp = fopen (argv[1], "rb")) == NULL) {
        perror ("fopen-argv[1]");
        return 1;
    }
    
    if ((jpgcnt = recoverjpgs(fp)))
        printf ("recovered %d .jpg files.\n", jpgcnt);
    else
        puts ("no jpg files recovered.");
        
    fclose (fp);
}