无限的do-while循环,应该是寻找JPEG标头

时间:2012-12-13 21:21:10

标签: c file-io do-while cs50

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

}

1 个答案:

答案 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.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;

你以文本模式打开文件,但实际应该在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节中找到。