程序从卡中恢复JPEG,但未通过CS50检查(pset4)
嘿,所有强大的程序员,恢复程序似乎工作得很好。我读过其他帖子。。。让Valgrind运行,并显示无泄漏。我有从000到049的所有50张照片。所以,我没有主意了。你们有吗?对不起,我的评论太多了,但我需要逐字逐句地跟踪发生了什么程序从卡中恢复JPEG,但未通过CS50检查(pset4),c,segmentation-fault,jpeg,cs50,recover,C,Segmentation Fault,Jpeg,Cs50,Recover,嘿,所有强大的程序员,恢复程序似乎工作得很好。我读过其他帖子。。。让Valgrind运行,并显示无泄漏。我有从000到049的所有50张照片。所以,我没有主意了。你们有吗?对不起,我的评论太多了,但我需要逐字逐句地跟踪发生了什么 #包括 #包括 #包括 //修复我们需要从中恢复的文件的名称 #定义名称\u原始\u文件“card.RAW” //块大小为512 #定义块大小512 //“jpg.000”+/n=>8个字符 #定义JPEG\u名称\u长度8 //jpeg文件名的格式,使用3个整数加0
#包括
#包括
#包括
//修复我们需要从中恢复的文件的名称
#定义名称\u原始\u文件“card.RAW”
//块大小为512
#定义块大小512
//“jpg.000”+/n=>8个字符
#定义JPEG\u名称\u长度8
//jpeg文件名的格式,使用3个整数加0
//看https://www.tutorialspoint.com/c_standard_library/c_function_sprintf.htm
#定义JPEG文件格式“%03d.jpg”
//bool检查报头是否是jpg的报头
bool是_jpeg(无符号字符头[])
{
/*这相当于说if(condition),return TRUE。return返回函数
*表达式范围内的任何值,在本例中,如果
*验证了具有等价性的表达式,否则为false*/
返回(头[0]==0xff
&&标题[1]==0xd8
&&标题[2]==0xff
&&(头[3]&0xf0)==0xe0);
}
int main(int argc,char*argv[])
{
如果(argc!=1)
{
//程序名后没有命令行参数
fprintf(stderr,“用法:./recover\n”);
返回1;
}
//打开存储卡文件
FILE*raw_FILE=fopen(名称为“r”);
if(原始文件==NULL)
{
fprintf(stderr,“无法打开文件%s。\n”,名称为\u原始\u文件);
返回1;
}
//存储新的JPG名称并对其进行计数
字符jpeg_文件名[jpeg_名称_长度];
int jpeg_已恢复_计数器=0;
//我们正在写入的当前打开的文件
FILE*jpeg\u FILE=NULL;
/*我们需要另一个缓冲区来存储card.raw中的jpg数据
*我们使用无符号字符,因为它基本上等同于
*说字节*/
无符号字符缓冲区[块大小];
/*在存储卡的每个块上循环,直到EOF。
*这个循环的作用是检查fread函数
*返回大小为1的512字节,并断开
*当存在小于512字节的块时:文件结束*/
while(fread(缓冲区,1,块大小,原始文件)=块大小)
{
//检查块的前4个字节是否对应于jpg
如果(是jpeg(缓冲区))
{
//是否有以前打开的jpg文件?如果是,请将其关闭。
//这不会关闭设置为NULL的第一个jpg文件
//在第43行。
如果(jpeg_文件!=NULL)
{
fclose(jpeg_文件);
}
//此行创建将存储的自定义文件名
//在jpeg_文件名数组中
sprintf(jpeg_文件名、jpeg_文件格式、jpeg_恢复计数器++);
//现在,我已将文件名存储在jpeg_文件名中
//我在读写模式下以该名称打开一个文件
jpeg_file=fopen(jpeg_文件名,“w+”);
//检查jpeg_文件是否已成功打开
如果(jpeg_文件==NULL)
{
fclose(原始文件);
fprintf(标准格式,“恢复:%s:创建文件时出错”,jpeg_文件名);
返回1;
}
}
/*在此阶段,如果未找到jpeg,则未打开jpeg_文件。
*因此,我创建了一个条件来跳过任何不属于
*通过检查是否有jpeg\u文件打开来创建jpeg。如果有jpeg
*打开文件,然后写入其中*/
如果(jpeg_文件!=NULL)
{
/*所以,如果我打开了一个jpeg文件,那么我想写入它
*但我也想确保写作成功,所以我将
*在检查
*函数(块大小)。如果写入未成功,则关闭所有内容
*和打印错误*/
if(写入(缓冲区,1,块大小,jpeg文件)!=块大小)
{
fclose(jpeg_文件);
fclose(原始文件);
fprintf(stderr,“恢复:%s:写入文件时出错”,jpeg\u文件名);
返回1;
}
}
}
//关闭上次打开的文件,检查我们是否打开过一个(想象一下card.raw没有jpeg!)
如果(jpeg_文件!=NULL)
{
fclose(jpeg_文件);
}
//从卡中读取时出错了吗?
if(ferror(原始文件))
{
fclose(原始文件);
fprintf(stderr,“恢复:%s:读取文件时出错,\n”,argv[0]);
返回1;
}
//否则,一切都好。
fclose(原始文件);
返回0;
}
尝试以二进制模式打开文件,例如“rb”而不是“r”。同样,对于编写,您显然会向程序传递一个参数,第一个if块将在该程序上执行。@samgak谢谢,但不起作用。我不明白。
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
//fix the name of the file we need to recover from
#define NAME_RAW_FILE "card.raw"
//block size is 512
#define BLOCK_SIZE 512
// "jpg.000" + /n => 8 chars
#define JPEG_NAME_LENGTH 8
// Format of the jpeg file name, using 3 integers padded with 0's
// see https://www.tutorialspoint.com/c_standard_library/c_function_sprintf.htm
#define JPEG_FILE_FORMAT "%03d.jpg"
// bool checking if the header is the one of a jpg
bool is_jpeg (unsigned char header [])
{
/* this is equivalent to saying if(condition), return TRUE. Return gives back to the function
* any value that comes out of the expression in its scope, in our case the value is TRUE if the
* expression with the equivalences is verified, else false. */
return (header[0] == 0xff
&& header[1] == 0xd8
&& header[2] == 0xff
&& (header[3] & 0xf0) == 0xe0);
}
int main(int argc, char *argv[])
{
if (argc != 1)
{
//no command line argument after programme name
fprintf(stderr, "Usage: ./recover\n");
return 1;
}
// open memory card file
FILE* raw_file = fopen(NAME_RAW_FILE, "r");
if (raw_file == NULL)
{
fprintf(stderr, "Could not open file %s.\n", NAME_RAW_FILE);
return 1;
}
// Store new JPG name and count them
char jpeg_filename[JPEG_NAME_LENGTH];
int jpeg_recoverd_counter = 0;
// Currently opened file we're writing to
FILE* jpeg_file = NULL;
/* we need another buffer to store our jpg data from card.raw
* we use unsigned chars because it's basically equivalent to
* saying bytes */
unsigned char buffer [BLOCK_SIZE];
/* loop over every block of the memory card until EOF.
* What this loop does is checking if the fread function
* returned 512 bytes of size 1, and breaks
* when there is a block of less than 512 bytes : end of file */
while (fread (buffer, 1, BLOCK_SIZE, raw_file) == BLOCK_SIZE)
{
// checks if the first 4 bytes of the block corresponds to a jpg
if (is_jpeg (buffer))
{
// Is there a previous jpg file open? If so, close it.
// This won't close your first jpg file which is set to NULL
// at line 43.
if (jpeg_file != NULL)
{
fclose(jpeg_file);
}
// This line creates a custom filename that will be stored
// in the jpeg_filename array
sprintf(jpeg_filename, JPEG_FILE_FORMAT, jpeg_recoverd_counter++);
// Now that I have the name of the file stored in jpeg_filename
// I open a file in reading and writing mode with that name
jpeg_file = fopen(jpeg_filename, "w+");
// Check that jpeg_file opened successfully
if(jpeg_file == NULL)
{
fclose(raw_file);
fprintf(stderr, "recover: %s: Error creating file", jpeg_filename);
return 1;
}
}
/* At this stage, if no jpeg was found then no jpeg_file was opened.
* so I create a condition to skip any initial bytes not belonging to
* a jpeg by checking if there is a jpeg_file open. If we do have a jpeg
* file open, then write into it */
if (jpeg_file != NULL)
{
/* So, if I do have a jpeg file opened, then I want to write into it
* but I also want to make sure that the writing succeeds, so I encase
* fwrite function in a if condition that checks the return value of the
* function (BLOCK_SIZE). If the writing did not succeed then close everything
* and print error. */
if(fwrite(buffer, 1, BLOCK_SIZE, jpeg_file) != BLOCK_SIZE)
{
fclose(jpeg_file);
fclose(raw_file);
fprintf(stderr, "recover: %s : Error writing the file\n", jpeg_filename);
return 1;
}
}
}
// Close last file open, checking that we ever opened one (imagine card.raw had no jpeg!)
if(jpeg_file != NULL)
{
fclose(jpeg_file);
}
// Was there an error reading from the card?
if(ferror(raw_file))
{
fclose(raw_file);
fprintf(stderr, "recover: %s: Error reading file\n", argv[0]);
return 1;
}
// Else, all good.
fclose(raw_file);
return 0;
}