-2

再一次,我正在尝试编写一个从 .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;
            }
        }
    }    
}  

}

4

1 回答 1

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.jpg01.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;

您以文本模式打开文件,但实际上应该像这样以二进制模式打开它(感谢@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 字节边界。

作为解决主要解析问题的一种方法,我建议改用状态变量,如下所示:

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");
  }
}

另请注意,我替换fgetcgetc- 对于某些编译器,它会更快,因为它是缓冲的 - 并且它具有与fgetc.

最后,正如 Jigsore 在评论中提到的,JPEG 解析实际上更复杂,您使用的序列是两个标记组合。基本的标记顺序和可选部分的解释可以在JPEG 规范的 B.2.1 ff 部分中找到。

于 2012-12-13T21:40:29.073 回答