-2

繰り返しますが、.raw ファイルから jpeg をコピーするプログラムを作成しようとしています。最初のヘッダー (0xffd8ffe0 または 0xffd8ffe1) が正常であることを検出し、ヘッダーを outptr に書き込み、jpeg データを 512 ビット チャンクでコピーします。do-while ループを記述して、512 ビット配列を読み取り、各配列をチェックして、(配列の最初の 4 バイトに) 新しいヘッダーが含まれていないことを確認します。 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は機能しません。それと残りのコードにはいくつかのエラーがあります。elsebreak

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文字以上を占めていたため、コメントの1つで述べたように他の変数によって使用されるメモリが上書きされ、回避されたためです-これはもう必要ありません:

// replace byte[0] since sprintf seems to make it 0 for some reason
byte[0] = 0xff;

ファイルをテキストモードで開きますが、実際には次のようにバイナリモードで開く必要があります(これを指摘してくれた@WhozCraigに感謝します):

FILE *inptr = fopen(READFILE, "rb");

2 番目のヘッダー検索ルーチンも機能しません。

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

fgetcまた、getc-一部のコンパイラでは、バッファリングされているため高速になります-と置き換えたことにも注意してくださいfgetc

最後に、Jigsore がコメントで述べたように、実際の JPEG 解析はより複雑であり、使用するシーケンスは 2 つのマーカーを組み合わせたものです。基本的なマーカーの順序とオプション部分の説明は、JPEG 仕様のセクション B.2.1 ff にあります。

于 2012-12-13T21:40:29.073 に答える