再一次,我正在尝试编写一个从.raw文件中复制jpeg的程序。它找到第一个标头(0xffd8ffe0或0xffd8ffe1),并继续将标头写入outptr,然后继续以512位块的形式复制jpeg数据。我已经尝试编写do-while循环,以便它读取512位数组并检查每个数组以确保它不包含新的标头(在数组的前四个字节中),这将使它停止并再次启动while循环,复制下一个循环,但它似乎永远不会找到另一个标题,即使我知道它在那里,它应该在最后的512位块之后立即出现。
#include <stdio.h>
#include <stdint.h>
#define READFILE "/home/cs50/pset5/card.raw"
int
main(void)
{
// open readfile
FILE *inptr = fopen(READFILE, "r");
if (inptr == NULL)
{
printf("Could not open file.\n");
return 1;
}
while (feof(inptr) == 0)
{
// counter for writefilename
int writeCounter = 0;
// find a header by iterating until it finds a 0xff
int byte[4];
if (byte[0] != 0xff)
byte[0] = fgetc(inptr);
else
{
// then check if the next byte is 0xd8, if not, look for the next 0xff
byte[1] = fgetc(inptr);
if (byte[1] != 0xd8)
break;
else
{
// then check if the next byte is 0xff, if not, ditto
byte[2] = fgetc(inptr);
if (byte[2] != 0xff)
break;
else
{
// then check if the next byte is 0xe0 or 0xe1, if not, ditto
byte[3] = fgetc(inptr);
if (byte[3] == 0xe0 || byte[3] == 0xe1)
{
// since it's a header, start writin'
// open writefile
char filename[7];
sprintf(filename, "0%.2d.jpg", writeCounter);
FILE *outptr = fopen(filename, "w");
writeCounter++;
// replace byte[0] since sprintf seems to make it 0 for some reason
byte[0] = 0xff;
// write the header that's in array byte[]
fwrite(&byte, 4, 1, outptr);
// write pixels in 64-byte chunks until a new header is found
char pixel[64];
do
{
fread(&pixel, 64, 1, inptr);
if (pixel[0] == 0xff && pixel[1] == 0xd8 && pixel[2] == 0xff && (pixel[3] == 0xe0 || pixel[3] == 0xe1))
{
fseek(inptr, -64, SEEK_CUR);
break;
}
else
fwrite(&pixel, 64, 1, outptr);
} while (pixel[0] != 0xff && pixel[1] != 0xd8 && pixel[2] != 0xff && (pixel[3] != 0xe0 || pixel[3] != 0xe1));
}
else
break;
}
}
}
}
}
答案 0 :(得分:2)
您撰写的if
- else
- break
构造无法发挥作用。它和其余代码中有几个错误:
byte
数组未初始化:
int byte[4];
// If you are here for the first time, byte[0] can be anything
if (byte[0] != 0xff)
byte[0] = fgetc(inptr);
如果找到了部分匹配(例如0xFF 0xD8
)并且您使用了break
,则循环会继续使用旧的byte
值,从而导致无限循环。
此外,正如他在评论中提到的H2CO3:
char filename[7];
sprintf(filename, "0%.2d.jpg", writeCounter);
我认为这应该是这样的(生成文件名00.jpg
,01.jpg
等等):
char filename[7];
sprintf(filename, "%02d.jpg", writeCounter);
这也解决了你以前的内存损坏问题(因为旧的文件名占用了7个以上的字符,所以其他变量使用的内存被覆盖了,正如你在其中一条评论中所说的那样并且解决了 - 这不应该是需要了:
// replace byte[0] since sprintf seems to make it 0 for some reason
byte[0] = 0xff;
你以文本模式打开文件,但实际应该在binary mode中打开它(感谢@WhozCraig指出这一点):
FILE *inptr = fopen(READFILE, "rb");
您的第二个标题搜索例程也不会起作用:
fread(&pixel, 64, 1, inptr);
if (pixel[0] == 0xff && pixel[1] == 0xd8 && pixel[2] == 0xff && (pixel[3] == 0xe0 || pixel[3] == 0xe1))
它只会捕获64字节块开头的序列,尽管它可能位于其他任何地方或跨越64字节边界。
作为解决主要解析问题的一种方法,我建议使用state变量,如下所示:
int state = 0;
int c;
while (feof(inptr) == 0) {
c = getc(inptr);
switch (state) {
case 0:
if (c == 0x00) {
state = 1;
}
case 1:
if (c == 0x01) {
state = 2;
} else {
state = 0;
}
case 2:
if (c == 0x02) {
state = 3;
} else {
state = 0;
}
case 3:
if ((c == 0x03) || (c == 0x04)) {
// We found 0x00010203 or 0x00010204, place more code here
state = 4; // Following states can parse data and look for other sequences
} else {
state = 0;
}
// More states here
default:
printf("This shouldn't happen\n");
}
}
另请注意,我将fgetc
替换为getc
- 对于某些编译器,它会更快,因为它被缓冲 - 并且它具有与fgetc
相同的语法。
最后,正如Jigsore在评论中提到的,JPEG解析实际上更复杂,你使用的序列是两个标记组合在一起。可选部件的基本标记顺序和说明可在JPEG specification,第B.2.1节中找到。