我不明白为什么 fread 不能正确填充我的缓冲区。 CS50回收
I cannot figure out why fread will not populate my buffer properly. CS50 recovery
我已经在 gdb 中检查这段代码好几个小时了。我知道 fread
正在返回适当数量的字节 (512
)。甚至检查 $eax
打印以确认。有没有人可以提示我我的逻辑有什么问题?
我认为 headers 可能从文件的开头偏移,所以我想在第一次读取时逐字节查找十六进制匹配并设置 fseek
会做诡计。没有这样的运气。打印匹配结果为 0
.
#include <stdio.h>
#include <getopt.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
typedef uint8_t BYTE;
typedef enum { false, true } boolean;
int main(int argc, char *argv[])
{
// get filenames from cml input and open file
char *infile = argv[optind];
char *fileName = "image";
FILE *rawData = fopen(infile, "r");
FILE *imgJPG = fopen(fileName, "w");
int match = 0;
int imgCnt = 0;
// buffer to hold 512 bytes of file data - FAT file system
BYTE *FATbuffer = (BYTE *)malloc(sizeof(BYTE) * 512);
if (rawData == NULL)
{
printf("Error processing file. Exiting...");
return 1;
}
// begin reading raw data and writing it to buffer
while (fread(FATbuffer, sizeof(BYTE), 512, rawData) == 512)
{
if (imgCnt == 0)
{
for (int c = 0; c < 512; c++)
{
if (FATbuffer[c + 0] == 0xff &&
FATbuffer[c + 1] == 0x8d &&
FATbuffer[c + 2] == 0xff)
{
fseek(rawData, c, SEEK_SET);
imgCnt++;
match++;
}
}
}
else
{
if (FATbuffer[0] == 0xff &&
FATbuffer[1] == 0x8d &&
FATbuffer[2] == 0xff &&
imgCnt > 0)
{
sprintf(fileName, "%d.jpg", imgCnt);
fclose(imgJPG);
imgCnt++;
}
if (imgJPG == NULL)
{
printf("Error processing file. Exiting...");
return 3;
}
fwrite(FATbuffer, sizeof(BYTE), 512, imgJPG);
}
}
printf("%d\n", match);
// file processed, free memory
free(FATbuffer);
return 0;
}
您的代码中存在多个问题:
您应该测试命令行参数是否可用(在处理选项之后,您可能删除了用于发布的代码)。
文件必须以二进制模式打开以避免可能的翻译结束。
您应该延迟打开 imgJPG
,直到找到 JPG header。
不需要分配FATbuffer
,定义一个自动存储的512字节数组就可以了
您一次扫描一个块来寻找 JPG 签名,但如果它跨越 512 字节边界并且您访问超出 FATbuffer
末尾的 2 个字节,您可能会错过签名c
大于 509 时的数组。
fseek(rawData, c, SEEK_SET);
将文件位置设置为距数组开头的偏移量,而不是从文件开头的偏移量。
sprintf(fileName, "%d.jpg", imgCnt);
尝试覆盖字符串常量。这具有未定义的行为。你可能是这个意思:
char fileName[64];
snprintf(fileName, sizeof fileName, "image%d.jpg", imgCnt);
FILE *imgJPG = fopen(fileName, "wb");
这是一个修改版本,可以提取嵌入数据流中任何位置的 JPG 文件:
#include <errno.h>
#include <getopt.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
// get filenames from cml input and open file
if (optind >= argc)
return 1;
char *infile = argv[optind];
FILE *rawData = fopen(infile, "rb"); // open the disk image
char fileName[64];
FILE *imgJPG = NULL;
int imgCnt = 0;
// buffer to hold 512 bytes of file data - FAT file system
// add an extra 2 bytes to match signature across sector boundaries
uint8_t FATbuffer[514];
// begin reading raw data into buffer
int pos = 2;
while (fread(FATbuffer + 2, 1, 512, rawData) == 512) {
for (int c = pos; c < 512; c++) {
if (FATbuffer[c + 0] == 0xff &&
FATbuffer[c + 1] == 0x8d &&
FATbuffer[c + 2] == 0xff) {
// found signature: skip to a new file
if (imgJPG) {
// write the end of the current image
fwrite(FATbuffer + pos, c - pos, 1, imgJPG);
fclose(imgJPG);
}
pos = c;
imgCnt++;
snprintf(fileName, sizeof fileName, "image%d.jpg", imgCnt);
imgJPG = fopen(fileName, "wb");
if (imgJPG == NULL) {
fprintf(stderr, "Cannot create file %s: %s\n",
fileName, strerror(errno));
return 3;
}
}
}
if (imgJPG) {
// write end of block to current image
fwrite(FATbuffer + pos, 512 - pos, 1, imgJPG);
}
// copy the last 2 bytes to test for signature overlapping blocks
FATbuffer[0] = FATbuffer[512];
FATbuffer[1] = FATbuffer[513];
// uncopied file data starts a 0 now.
pos = 0;
}
if (imgJPG) {
// write last 2 bytes to current image
fwrite(FATbuffer, 2, 1, imgJPG);
fclose(imgJPG);
}
printf("%d\n", imgCnt != 0);
printf("%d images extracted\n", imgCnt);
return 0;
}
如果可以假定签名位于扇区的开头,则可以简化代码:
#include <errno.h>
#include <getopt.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
// get filenames from cml input and open file
if (optind >= argc)
return 1;
char *infile = argv[optind];
FILE *rawData = fopen(infile, "rb"); // open the disk image
char fileName[64];
FILE *imgJPG = NULL;
int imgCnt = 0;
// buffer to hold 512 bytes of file data - FAT file system
uint8_t FATbuffer[512];
// begin reading raw data into buffer
while (fread(FATbuffer, 1, 512, rawData) == 512) {
if (FATbuffer[c + 0] == 0xff &&
FATbuffer[c + 1] == 0x8d &&
FATbuffer[c + 2] == 0xff) {
// found signature: skip to a new file
if (imgJPG) {
fclose(imgJPG);
}
imgCnt++;
snprintf(fileName, sizeof fileName, "image%d.jpg", imgCnt);
imgJPG = fopen(fileName, "wb");
if (imgJPG == NULL) {
fprintf(stderr, "Cannot create file %s: %s\n",
fileName, strerror(errno));
return 3;
}
}
if (imgJPG) {
// write end of block to current image
fwrite(FATbuffer, 512, 1, imgJPG);
}
}
if (imgJPG) {
fclose(imgJPG);
}
printf("%d\n", imgCnt != 0);
printf("%d images extracted\n", imgCnt);
return 0;
}
我已经在 gdb 中检查这段代码好几个小时了。我知道 fread
正在返回适当数量的字节 (512
)。甚至检查 $eax
打印以确认。有没有人可以提示我我的逻辑有什么问题?
我认为 headers 可能从文件的开头偏移,所以我想在第一次读取时逐字节查找十六进制匹配并设置 fseek
会做诡计。没有这样的运气。打印匹配结果为 0
.
#include <stdio.h>
#include <getopt.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
typedef uint8_t BYTE;
typedef enum { false, true } boolean;
int main(int argc, char *argv[])
{
// get filenames from cml input and open file
char *infile = argv[optind];
char *fileName = "image";
FILE *rawData = fopen(infile, "r");
FILE *imgJPG = fopen(fileName, "w");
int match = 0;
int imgCnt = 0;
// buffer to hold 512 bytes of file data - FAT file system
BYTE *FATbuffer = (BYTE *)malloc(sizeof(BYTE) * 512);
if (rawData == NULL)
{
printf("Error processing file. Exiting...");
return 1;
}
// begin reading raw data and writing it to buffer
while (fread(FATbuffer, sizeof(BYTE), 512, rawData) == 512)
{
if (imgCnt == 0)
{
for (int c = 0; c < 512; c++)
{
if (FATbuffer[c + 0] == 0xff &&
FATbuffer[c + 1] == 0x8d &&
FATbuffer[c + 2] == 0xff)
{
fseek(rawData, c, SEEK_SET);
imgCnt++;
match++;
}
}
}
else
{
if (FATbuffer[0] == 0xff &&
FATbuffer[1] == 0x8d &&
FATbuffer[2] == 0xff &&
imgCnt > 0)
{
sprintf(fileName, "%d.jpg", imgCnt);
fclose(imgJPG);
imgCnt++;
}
if (imgJPG == NULL)
{
printf("Error processing file. Exiting...");
return 3;
}
fwrite(FATbuffer, sizeof(BYTE), 512, imgJPG);
}
}
printf("%d\n", match);
// file processed, free memory
free(FATbuffer);
return 0;
}
您的代码中存在多个问题:
您应该测试命令行参数是否可用(在处理选项之后,您可能删除了用于发布的代码)。
文件必须以二进制模式打开以避免可能的翻译结束。
您应该延迟打开
imgJPG
,直到找到 JPG header。不需要分配
FATbuffer
,定义一个自动存储的512字节数组就可以了您一次扫描一个块来寻找 JPG 签名,但如果它跨越 512 字节边界并且您访问超出
FATbuffer
末尾的 2 个字节,您可能会错过签名c
大于 509 时的数组。fseek(rawData, c, SEEK_SET);
将文件位置设置为距数组开头的偏移量,而不是从文件开头的偏移量。sprintf(fileName, "%d.jpg", imgCnt);
尝试覆盖字符串常量。这具有未定义的行为。你可能是这个意思:char fileName[64]; snprintf(fileName, sizeof fileName, "image%d.jpg", imgCnt); FILE *imgJPG = fopen(fileName, "wb");
这是一个修改版本,可以提取嵌入数据流中任何位置的 JPG 文件:
#include <errno.h>
#include <getopt.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
// get filenames from cml input and open file
if (optind >= argc)
return 1;
char *infile = argv[optind];
FILE *rawData = fopen(infile, "rb"); // open the disk image
char fileName[64];
FILE *imgJPG = NULL;
int imgCnt = 0;
// buffer to hold 512 bytes of file data - FAT file system
// add an extra 2 bytes to match signature across sector boundaries
uint8_t FATbuffer[514];
// begin reading raw data into buffer
int pos = 2;
while (fread(FATbuffer + 2, 1, 512, rawData) == 512) {
for (int c = pos; c < 512; c++) {
if (FATbuffer[c + 0] == 0xff &&
FATbuffer[c + 1] == 0x8d &&
FATbuffer[c + 2] == 0xff) {
// found signature: skip to a new file
if (imgJPG) {
// write the end of the current image
fwrite(FATbuffer + pos, c - pos, 1, imgJPG);
fclose(imgJPG);
}
pos = c;
imgCnt++;
snprintf(fileName, sizeof fileName, "image%d.jpg", imgCnt);
imgJPG = fopen(fileName, "wb");
if (imgJPG == NULL) {
fprintf(stderr, "Cannot create file %s: %s\n",
fileName, strerror(errno));
return 3;
}
}
}
if (imgJPG) {
// write end of block to current image
fwrite(FATbuffer + pos, 512 - pos, 1, imgJPG);
}
// copy the last 2 bytes to test for signature overlapping blocks
FATbuffer[0] = FATbuffer[512];
FATbuffer[1] = FATbuffer[513];
// uncopied file data starts a 0 now.
pos = 0;
}
if (imgJPG) {
// write last 2 bytes to current image
fwrite(FATbuffer, 2, 1, imgJPG);
fclose(imgJPG);
}
printf("%d\n", imgCnt != 0);
printf("%d images extracted\n", imgCnt);
return 0;
}
如果可以假定签名位于扇区的开头,则可以简化代码:
#include <errno.h>
#include <getopt.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
// get filenames from cml input and open file
if (optind >= argc)
return 1;
char *infile = argv[optind];
FILE *rawData = fopen(infile, "rb"); // open the disk image
char fileName[64];
FILE *imgJPG = NULL;
int imgCnt = 0;
// buffer to hold 512 bytes of file data - FAT file system
uint8_t FATbuffer[512];
// begin reading raw data into buffer
while (fread(FATbuffer, 1, 512, rawData) == 512) {
if (FATbuffer[c + 0] == 0xff &&
FATbuffer[c + 1] == 0x8d &&
FATbuffer[c + 2] == 0xff) {
// found signature: skip to a new file
if (imgJPG) {
fclose(imgJPG);
}
imgCnt++;
snprintf(fileName, sizeof fileName, "image%d.jpg", imgCnt);
imgJPG = fopen(fileName, "wb");
if (imgJPG == NULL) {
fprintf(stderr, "Cannot create file %s: %s\n",
fileName, strerror(errno));
return 3;
}
}
if (imgJPG) {
// write end of block to current image
fwrite(FATbuffer, 512, 1, imgJPG);
}
}
if (imgJPG) {
fclose(imgJPG);
}
printf("%d\n", imgCnt != 0);
printf("%d images extracted\n", imgCnt);
return 0;
}