Pset4 (cs50) 恢复无法正常工作。它可以编译,但不会恢复超过 2 个 jpeg。检查 JPEG 签名有问题吗?
Pset4 (cs50) recover does not work properly. It compiles, but does not recover more than 2 jpegs. Is something wrong with checking for JPEG signature?
我正在学习如何编码,但我完全没有这方面的经验。我已经成功进入 PSET4 并坚持恢复。我已经在网上阅读了关于这个问题的所有内容,我发现很多人都有与我相似的代码并且它有效。对我不起作用。请看一下并提示我做错了什么以及如何纠正。
这是关于 pset4 的所有内容 recover i downloaded their card.raw from here card.raw
/** recovering JPEG files from a memory card
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
typedef uint8_t BYTE;
int main(int argc, char* argv[])
{
// ensure proper usage
if (argc != 2)
{
fprintf(stderr,
"Usage: ./recover infile (the name of a forensic image from which to recover JPEGs)\n");
return 1;
}
// open input file (forensic image)
FILE* inptr = fopen(argv[1], "r");
if (inptr == NULL)
{
fprintf(stderr, "Could not open %s.\n", argv[1]);
return 2;
}
FILE* outptr = NULL;
// create a pointer array of 512 elements to store 512 bytes from the memory card
BYTE* buffer = malloc(sizeof(BYTE) * 512);
if (buffer == NULL)
{
return 3;
}
// count amount of jpeg files found
int jpeg = 0;
// string for a file name using sprintf
char filename[8] = { 0 };
// read memory card untill the end of file
while (fread(buffer, sizeof(BYTE) * 512, 1, inptr) != 0)
{
// check if jpeg is found
if (buffer[0] == 0xff && buffer[1] == 0xd8 && buffer[2] == 0xff
&& (buffer[3] >= 0xe0 || buffer[3] <= 0xef))
{
if (jpeg > 0)
{
fclose(outptr);
}
sprintf(filename, "%03d.JPEG", jpeg);
outptr = fopen(filename, "w");
jpeg++;
}
if (jpeg > 0)
{
fwrite(buffer, sizeof(BYTE) * 512, 1, outptr);
}
}
// free memory
free(buffer);
// close filename
fclose(outptr);
// close input file (forensic image)
fclose(inptr);
return 0;
}
主要问题是您调用了未定义的行为,因为 filename
不够大。 sprintf()
你的代码需要 9 和 17 个字节,但你只有 8 个。所以你有缓冲区溢出。
只需更改:
char filename[8] = { 0 };
到
char filename[17] = { 0 };
因为,你使用了一个int
,这个值是实现定义的,但是在很多系统中有一个32位的int
。所以可能的值在 -2^31
和 2^31 - 1
之间,最大为 11 char
s (-2147483648)。我们在“.JPEG”中添加 char
s 的数量,5。我们有 16,但您忘记了 c 字符串的空终止字节。所以我们最多17人。
现代编译器警告您:gcc 版本 7.1.1 20170516 (GCC):
In function ‘main’:
warning: ‘sprintf’ writing a terminating nul past the end of the destination [-Wformat-overflow ]
sprintf(filename, "%03d.JPEG", jpeg++);
^
note: ‘sprintf’ output between 9 and 17 bytes into a destination of size 8
sprintf(filename, "%03d.JPEG", jpeg++);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
此外,您的 typedef
没用,因为 char
世界在 C 中始终是一个字节。除此之外,您不需要一个字节,而是一个八位字节,例如 char
, uint8_t
在 C 中总是一个八位字节。所以你不需要 typedef
.
再说一件事,你分配了你的缓冲区,但它没有用,因为你的缓冲区有一个恒定的大小。所以直接创建数组就更简单了。
#include <stdint.h>
#include <stdio.h>
int main(int argc, char *argv[]) {
if (argc != 2) {
fprintf(stderr, "Usage: ./recover infile (the name of a forensic image "
"from which to recover JPEGs)\n");
return 1;
}
FILE *inptr = fopen(argv[1], "r");
if (inptr == NULL) {
fprintf(stderr, "Could not open %s.\n", argv[1]);
return 2;
}
FILE *outptr = NULL;
uint8_t buffer[512];
size_t const buffer_size = sizeof buffer / sizeof *buffer;
size_t jpeg = 0;
while (fread(buffer, sizeof *buffer, buffer_size, inptr) == buffer_size) {
if (buffer[0] == 0xff && buffer[1] == 0xd8 && buffer[2] == 0xff &&
buffer[3] == 0xe0) {
if (outptr != NULL) {
fclose(outptr);
}
char filename[26];
sprintf(filename, "%03zu.JPEG", jpeg++);
outptr = fopen(filename, "w");
}
if (outptr != NULL) {
fwrite(buffer, sizeof *buffer, buffer_size, outptr);
}
}
if (outptr != NULL) {
fwrite(buffer, sizeof *buffer, buffer_size, outptr);
}
if (outptr != NULL) {
fclose(outptr);
}
fclose(inptr);
}
注意:这个例子显然并不完美,这将更好地为 jpeg 文件制作一个真正的解析器,以获得更好的控制流程。在这里我们假设一切都会正确。
您怎么知道 JPEG 图像的实例总是以“\n”结尾?或者更好的是,您如何知道 JPEG 图像将是 512 的精确倍数?
你不知道。
因此发布的代码需要计算实际值或使用某种方法对任何特定 JPEG 实例最后一次调用 fread()
,以在该图像末尾停止读取,
然后检查下一个 JPEG 图像的 ID 字节将找到下一个图像。
否则,下一个图像的开头已经写入先前的输出文件,新图像的检查将失败。
一般来说,这会导致最后创建的文件包含多个图像。
此 link:“https://en.wikipedia.org/wiki/JPEG_File_Interchange_Format”是描述 JPEG 文件格式的网页。
我用过的每一台数码相机,SD卡上都有一个目录,里面有所有的文件。
建议使用该目录和 linked 网页中的信息来查找每个 JPEG 图像并确定何时遇到该图像的末尾。 (即 0xFF 0xD9)
我正在学习如何编码,但我完全没有这方面的经验。我已经成功进入 PSET4 并坚持恢复。我已经在网上阅读了关于这个问题的所有内容,我发现很多人都有与我相似的代码并且它有效。对我不起作用。请看一下并提示我做错了什么以及如何纠正。 这是关于 pset4 的所有内容 recover i downloaded their card.raw from here card.raw
/** recovering JPEG files from a memory card
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
typedef uint8_t BYTE;
int main(int argc, char* argv[])
{
// ensure proper usage
if (argc != 2)
{
fprintf(stderr,
"Usage: ./recover infile (the name of a forensic image from which to recover JPEGs)\n");
return 1;
}
// open input file (forensic image)
FILE* inptr = fopen(argv[1], "r");
if (inptr == NULL)
{
fprintf(stderr, "Could not open %s.\n", argv[1]);
return 2;
}
FILE* outptr = NULL;
// create a pointer array of 512 elements to store 512 bytes from the memory card
BYTE* buffer = malloc(sizeof(BYTE) * 512);
if (buffer == NULL)
{
return 3;
}
// count amount of jpeg files found
int jpeg = 0;
// string for a file name using sprintf
char filename[8] = { 0 };
// read memory card untill the end of file
while (fread(buffer, sizeof(BYTE) * 512, 1, inptr) != 0)
{
// check if jpeg is found
if (buffer[0] == 0xff && buffer[1] == 0xd8 && buffer[2] == 0xff
&& (buffer[3] >= 0xe0 || buffer[3] <= 0xef))
{
if (jpeg > 0)
{
fclose(outptr);
}
sprintf(filename, "%03d.JPEG", jpeg);
outptr = fopen(filename, "w");
jpeg++;
}
if (jpeg > 0)
{
fwrite(buffer, sizeof(BYTE) * 512, 1, outptr);
}
}
// free memory
free(buffer);
// close filename
fclose(outptr);
// close input file (forensic image)
fclose(inptr);
return 0;
}
主要问题是您调用了未定义的行为,因为 filename
不够大。 sprintf()
你的代码需要 9 和 17 个字节,但你只有 8 个。所以你有缓冲区溢出。
只需更改:
char filename[8] = { 0 };
到
char filename[17] = { 0 };
因为,你使用了一个int
,这个值是实现定义的,但是在很多系统中有一个32位的int
。所以可能的值在 -2^31
和 2^31 - 1
之间,最大为 11 char
s (-2147483648)。我们在“.JPEG”中添加 char
s 的数量,5。我们有 16,但您忘记了 c 字符串的空终止字节。所以我们最多17人。
现代编译器警告您:gcc 版本 7.1.1 20170516 (GCC):
In function ‘main’:
warning: ‘sprintf’ writing a terminating nul past the end of the destination [-Wformat-overflow ]
sprintf(filename, "%03d.JPEG", jpeg++);
^
note: ‘sprintf’ output between 9 and 17 bytes into a destination of size 8
sprintf(filename, "%03d.JPEG", jpeg++);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
此外,您的 typedef
没用,因为 char
世界在 C 中始终是一个字节。除此之外,您不需要一个字节,而是一个八位字节,例如 char
, uint8_t
在 C 中总是一个八位字节。所以你不需要 typedef
.
再说一件事,你分配了你的缓冲区,但它没有用,因为你的缓冲区有一个恒定的大小。所以直接创建数组就更简单了。
#include <stdint.h>
#include <stdio.h>
int main(int argc, char *argv[]) {
if (argc != 2) {
fprintf(stderr, "Usage: ./recover infile (the name of a forensic image "
"from which to recover JPEGs)\n");
return 1;
}
FILE *inptr = fopen(argv[1], "r");
if (inptr == NULL) {
fprintf(stderr, "Could not open %s.\n", argv[1]);
return 2;
}
FILE *outptr = NULL;
uint8_t buffer[512];
size_t const buffer_size = sizeof buffer / sizeof *buffer;
size_t jpeg = 0;
while (fread(buffer, sizeof *buffer, buffer_size, inptr) == buffer_size) {
if (buffer[0] == 0xff && buffer[1] == 0xd8 && buffer[2] == 0xff &&
buffer[3] == 0xe0) {
if (outptr != NULL) {
fclose(outptr);
}
char filename[26];
sprintf(filename, "%03zu.JPEG", jpeg++);
outptr = fopen(filename, "w");
}
if (outptr != NULL) {
fwrite(buffer, sizeof *buffer, buffer_size, outptr);
}
}
if (outptr != NULL) {
fwrite(buffer, sizeof *buffer, buffer_size, outptr);
}
if (outptr != NULL) {
fclose(outptr);
}
fclose(inptr);
}
注意:这个例子显然并不完美,这将更好地为 jpeg 文件制作一个真正的解析器,以获得更好的控制流程。在这里我们假设一切都会正确。
您怎么知道 JPEG 图像的实例总是以“\n”结尾?或者更好的是,您如何知道 JPEG 图像将是 512 的精确倍数?
你不知道。
因此发布的代码需要计算实际值或使用某种方法对任何特定 JPEG 实例最后一次调用 fread()
,以在该图像末尾停止读取,
然后检查下一个 JPEG 图像的 ID 字节将找到下一个图像。
否则,下一个图像的开头已经写入先前的输出文件,新图像的检查将失败。
一般来说,这会导致最后创建的文件包含多个图像。
此 link:“https://en.wikipedia.org/wiki/JPEG_File_Interchange_Format”是描述 JPEG 文件格式的网页。
我用过的每一台数码相机,SD卡上都有一个目录,里面有所有的文件。
建议使用该目录和 linked 网页中的信息来查找每个 JPEG 图像并确定何时遇到该图像的末尾。 (即 0xFF 0xD9)