2

一度に1つのPNGファイルを含むバイナリファイルがあります(バイナリファイルはDLLでもEXEでもなく、通常のものではなく、さまざまなテキスト情報、PNGファイル、およびその他のものを含むファイルです。 PNG ファイルは、この種類のファイルを処理するプログラムで表示できます)。これらのファイルを実行するこのプログラムのソースはありません。私の仕事は、このPNGファイルをバイナリファイルから抽出して、表示またはPNGとして保存することです。これらのファイルの一部 (ファイルの約 50% としましょう) で動作するコードを書きましたが、別のファイルでは動作しません。動作していないファイルでも、このファイルを作成したプログラムは含まれている画像を表示できるため、画像はすべてのファイル内に確実に有効ですが、一部のファイルではコードが機能しません。

一部の画像は、おそらく別の形式、おそらくエンコーディング タイプを持っているようです (私はすでにすべての異なるエンコーディング タイプを試しましたが、何も成功しませんでした)。これが私のコードです(画像が常に読み取り可能になるように何を変更すればよいか誰かが教えてくれることを願っています)。

私のコードは何をしますか:PNG画像の既知の開始文字列「‰PNG」と既知の終了文字列「IEND®B`‚」を見つけます。この文字列は、同じ PNG を含む私のバイナリ ファイルのいずれかにあります。次に、私のコードは、開始と終了 + 開始と終了シーケンスの間の文字列を取得し、Encoding.Default でファイルに保存します。この方法で抽出された PNG ファイルのほとんどはイメージ ビューアーで表示できますが、約 50% は無効です。エディターで開いて文字を実際の画像と比較すると、画像は問題ないように見えます。Soar 私は、どのシンボルが間違った画像形式の理由なのか見当がつきません。

必要に応じて、詳細情報を提供します。ここに私のコード:

private void button2_Click(object sender, EventArgs e)
    {
        string ReadFile1 = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory), "file.dat");
        string WriteFile1 = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory), "test.png");
        string TMP = File.ReadAllText(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory), ReadFile1), Encoding.Default); //System.Text.Encoding.GetEncoding(1251)
        int start1 = TMP.IndexOf("PNG", 0 ,StringComparison.Ordinal);
        if (start1 == 0) { return; }
        int end1 = TMP.IndexOf("IEND", StringComparison.Ordinal);
        string PNG = TMP.Substring(start1 - 1, (end1 + 9) - start1);
        File.WriteAllText(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory), "test.png"), PNG, Encoding.Default);
    }

また、最初にバイナリ メソッドで PNG を取得することを考え、このコードを使用しましたが、文字列を読み取るだけでまったく同じ結果が得られました。ここに私の以前のコードがあります。比較する文字列を使用して、バイト配列内の位置をシークしました。私はバイナリコードに運がなかった...

 byte[] by;
        // 1.
        // Open file with a BinaryReader.
        using (BinaryReader b = new BinaryReader(File.Open(ReadFile1, FileMode.Open), Encoding.Default))
        {
            // 2.
            // Variables for our position.
            int pos = start1 - 1;           //I determine the right positions before doing this
            int required = (end1 + 9) - start1; 

            // 3.
            // Seek to our required position.
            b.BaseStream.Seek(pos, SeekOrigin.Begin);

            // 4.
            // Read the next 2000 bytes.
            by = b.ReadBytes(required);
            b.Close();
        }

        FileStream writeStream;
        writeStream = new FileStream(WriteFile1, FileMode.Create);
        BinaryWriter writeBinay = new BinaryWriter(writeStream, Encoding.Default);
        writeBinay.Write(by);
        writeBinay.Close(); */
4

3 に答える 3

5

ファイルをテキスト ファイルとして読み取らないでください。内容物に変形が生じる場合があります。代わりに を使用してみてFile.ReadAllBytes、PNG ファイルの先頭と末尾のバイト シーケンスを検索し、その領域のバイトを書き出す必要があります。

バイト配列で一連のバイトを検索するには、次のようなコードを使用できます。

private static int IndexOf(byte[] array, byte[] sequence, int startIndex)
{
    if (sequence.Length == 0)
        return -1;

    int found = 0;
    for (int i = startIndex; i < array.Length; i++)
    {
        if (array[i] == sequence[found])
        {
            if (++found == sequence.Length)
            {
                return i - found + 1;
            }
        }
        else
        {
            found = 0;
        }
    }

    return -1;
}

private void button2_Click(object sender, EventArgs e) 
{ 
    string ReadFile1 = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory), "file.dat"); 
    string WriteFile1 = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory), "test.png"); 

    byte[] TMP = File.ReadAllBytes(ReadFile1);

    byte[] pngStartSequence = new byte[] { 0x89, 0x50, 0x4E, 0x47 };
    byte[] pngEndSequence = new byte[] { 0x49, 0x46, 0x4E, 0x44 };

    int start1 = IndexOf(TMP, pngStartSequence, 0);
    if (start1 == -1)
    {
       // no PNG present
       MessageBox.Show("Could not find PNG header");
       return;
    }

    int end1 = IndexOf(TMP, pngEndSequence, start1 + pngStartSequence.Length);
    if (end1 == -1)
    {
       // no IEND present
       MessageBox.Show("Could not find PNG footer");
       return;
    }

    int pngLength = end1 - start1 + 8;
    byte[] PNG = new byte[pngLength];

    Array.Copy(TMP, start1, PNG, 0, pngLength);

    File.WriteAllBytes(WriteFile1, PNG); 
} 
于 2012-07-14T22:16:09.197 に答える
5

PNG ファイルはバイナリです。エンコーディングを使用してそれらを読み取ると、情報が失われ、プログラムの出力は有効な PNG ファイルではなくなります。詳細な説明とコード サンプルについては、PNG でのチャンクの使用を参照してください。

詳細については、PNG仕様: ファイル構造も参照してください。

于 2012-07-14T22:17:29.430 に答える
1

とを使用File.ReadAllBytesFile.WriteAllBytesます。テキストとしての読み取りと書き込みは、エンコードによって影響を受ける場合があります。

次のようなパターンを見つけるためにJb Evain アルゴリズムを使用できます。Byte Array

static void Main()
{
    // PNG file signature
    var startPattern = new byte[] { 137, 80, 78, 71, 13, 10, 26, 105 };
    var data = File.ReadAllBytes("png file");

    var start = data.Locate(startPattern);
    // and end like this
}    

public static int[] Locate(this byte[] self, byte[] candidate)
{
    if (IsEmptyLocate(self, candidate))
        return Empty;

    var list = new List<int>();

    for (int i = 0; i < self.Length; i++)
    {
        if (!IsMatch(self, i, candidate))
            continue;

        list.Add(i);
    }

    return list.Count == 0 ? Empty : list.ToArray();
}

static bool IsMatch(byte[] array, int position, byte[] candidate)
{
    if (candidate.Length > (array.Length - position))
        return false;

    for (int i = 0; i < candidate.Length; i++)
        if (array[position + i] != candidate[i])
            return false;

    return true;
}

static readonly int[] Empty = new int[0];

static bool IsEmptyLocate(byte[] array, byte[] candidate)
{
    return array == null
            || candidate == null
            || array.Length == 0
            || candidate.Length == 0
            || candidate.Length > array.Length;
}
于 2012-07-14T22:15:40.940 に答える