34

プログラムを使用してさまざまな情報をすべて読み取ることにより、STFS ファイル形式の理解を深めようとしています。どのオフセットにどの情報が含まれているかを参照する Web サイトを使用して、バイナリ リーダーがファイルを調べて値を正しい変数に配置するコードを書きました。

問題は、すべてのデータがビッグ エンディアンであることが前提であり、バイナリ リーダーが読み取るものはすべてリトル エンディアンであるということです。それで、これを修正するための最良の方法は何ですか?

反転したバイト配列を返す Binary リーダーの模倣クラスを作成できますか? すべてを書き直す必要がないように、クラス インスタンスをビッグ エンディアンで読み取るように変更できるものはありますか?

どんな助けでも大歓迎です。

編集: Encoding.BigEndianUnicode をパラメーターとして追加しようとしましたが、それでもリトル エンディアンを読み取ります。

4

7 に答える 7

40

私は通常、自分の質問に答える人ではありませんが、いくつかの簡単なコードでまさに望んでいたことを達成しました。

class BinaryReader2 : BinaryReader { 
    public BinaryReader2(System.IO.Stream stream)  : base(stream) { }

    public override int ReadInt32()
    {
        var data = base.ReadBytes(4);
        Array.Reverse(data);
        return BitConverter.ToInt32(data, 0);
    }

    public Int16 ReadInt16()
    {
        var data = base.ReadBytes(2);
        Array.Reverse(data);
        return BitConverter.ToInt16(data, 0);
    }

    public Int64 ReadInt64()
    {
        var data = base.ReadBytes(8);
        Array.Reverse(data);
        return BitConverter.ToInt64(data, 0);
    }

    public UInt32 ReadUInt32()
    {
        var data = base.ReadBytes(4);
        Array.Reverse(data);
        return BitConverter.ToUInt32(data, 0);
    }

}

それが私が望んでいることはわかっていましたが、それを書く方法がわかりませんでした。このページを見つけて役に立ちました: http://www.codekeep.net/snippets/870c4ab3-419b-4dd2-a950-6d45beaf1295.aspx

于 2011-12-23T22:29:44.037 に答える
14

IMHOは、別のクラスを新しくする必要がなく、ビッグエンディアンの呼び出しを明確にし、ビッグエンディアンとリトルエンディアンの呼び出しをストリームに混在させることができるため、少し良い答えです。

public static class Helpers
{
  // Note this MODIFIES THE GIVEN ARRAY then returns a reference to the modified array.
  public static byte[] Reverse(this byte[] b)
  {
    Array.Reverse(b);
    return b;
  }

  public static UInt16 ReadUInt16BE(this BinaryReader binRdr)
  {
    return BitConverter.ToUInt16(binRdr.ReadBytesRequired(sizeof(UInt16)).Reverse(), 0);
  }

  public static Int16 ReadInt16BE(this BinaryReader binRdr)
  {
    return BitConverter.ToInt16(binRdr.ReadBytesRequired(sizeof(Int16)).Reverse(), 0);
  }

  public static UInt32 ReadUInt32BE(this BinaryReader binRdr)
  {
    return BitConverter.ToUInt32(binRdr.ReadBytesRequired(sizeof(UInt32)).Reverse(), 0);
  }

  public static Int32 ReadInt32BE(this BinaryReader binRdr)
  {
    return BitConverter.ToInt32(binRdr.ReadBytesRequired(sizeof(Int32)).Reverse(), 0);
  }

  public static byte[] ReadBytesRequired(this BinaryReader binRdr, int byteCount)
  {
    var result = binRdr.ReadBytes(byteCount);

    if (result.Length != byteCount)
      throw new EndOfStreamException(string.Format("{0} bytes required from stream, but only {1} returned.", byteCount, result.Length));

    return result;
  }
}
于 2013-03-07T15:06:46.430 に答える
8

STFS には詳しくありませんが、エンディアンの変更は比較的簡単です。「Network Order」はビッグ エンディアンなので、ネットワークからホストの順序に変換するだけです。

それを行うコードが既にあるので、これは簡単です。ここで説明されているように、 を見てくださいIPAddress.NetworkToHostOrder: ntohs() と ntohl() は同等ですか?

于 2011-12-23T21:49:29.710 に答える
6

私の意見では、これを行うには注意が必要です。BigEndian から LittleEndian に変換する理由は、読み取られるバイトが BigEndian であり、それらに対して計算する OS が LittleEndian で動作している場合です。

C# はもはやウィンドウのみの言語ではありません。Mono のようなポートと、Windows Phone 7/8、Xbox 360/Xbox One、Windwos CE、Windows 8 Mobile、Linux With MONO、Apple with MONO などの他の Microsoft プラットフォームも使用できます。この場合、チェックを行わずにコードを変換すると、自分自身を台無しにすることになります。

BitConverter には既に「IsLittleEndian」というフィールドがあり、これを使用して動作環境が LittleEndian であるかどうかを判断できます。次に、条件付きで逆にすることができます。

そのため、実際には、大きなクラスを作成する代わりに、いくつかの byte[] 拡張機能を作成しました。

    /// <summary>
    /// Get's a byte array from a point in a source byte array and reverses the bytes. Note, if the current platform is not in LittleEndian the input array is assumed to be BigEndian and the bytes are not returned in reverse order
    /// </summary>
    /// <param name="byteArray">The source array to get reversed bytes for</param>
    /// <param name="startIndex">The index in the source array at which to begin the reverse</param>
    /// <param name="count">The number of bytes to reverse</param>
    /// <returns>A new array containing the reversed bytes, or a sub set of the array not reversed.</returns>
    public static byte[] ReverseForBigEndian(this byte[] byteArray, int startIndex, int count)
    {
        if (BitConverter.IsLittleEndian)
            return byteArray.Reverse(startIndex, count);
        else
            return byteArray.SubArray(startIndex, count);

    }

    public static byte[] Reverse(this byte[] byteArray, int startIndex, int count)
    {
        byte[] ret = new byte[count];
        for (int i = startIndex + (count - 1); i >= startIndex; --i)
        {
            byte b = byteArray[i];
            ret[(startIndex + (count - 1)) - i] = b;
        }
        return ret;
    }

    public static byte[] SubArray(this byte[] byteArray, int startIndex, int count)
    {
        byte[] ret = new byte[count];
        for (int i = 0; i < count; ++i)            
            ret[0] = byteArray[i + startIndex];
        return ret;
    }

次のコード例を想像してみてください。

byte[] fontBytes = byte[240000]; //some data loaded in here, E.G. a TTF TrueTypeCollection font file. (which is in BigEndian)

int _ttcVersionMajor = BitConverter.ToUint16(fontBytes.ReverseForBigEndian(4, 2), 0);

//output
_ttcVersionMajor = 1 //TCCHeader is version 1
于 2014-02-06T07:39:05.827 に答える
0

BinaryPrimitivesクラスを使用することをお勧めします

        public override double ReadDouble()
        {
            return BinaryPrimitives.ReadDoubleBigEndian(ReadBytes(8));
        }

        public override short ReadInt16()
        {
            return BinaryPrimitives.ReadInt16BigEndian(ReadBytes(2));
        }

        public override int ReadInt32()
        {
            return BinaryPrimitives.ReadInt32BigEndian(ReadBytes(4));
        }

        public override long ReadInt64()
        {
            return BinaryPrimitives.ReadInt64BigEndian(ReadBytes(8));
        }

        public override float ReadSingle()
        {
            return BinaryPrimitives.ReadSingleBigEndian(ReadBytes(4));
        }

        public override ushort ReadUInt16()
        {
            return BinaryPrimitives.ReadUInt16BigEndian(ReadBytes(2));
        }

        public override uint ReadUInt32()
        {
            return BinaryPrimitives.ReadUInt32BigEndian(ReadBytes(4));
        }

        public override ulong ReadUInt64()
        {
            return BinaryPrimitives.ReadUInt64BigEndian(ReadBytes(8));
        }
于 2022-02-09T11:08:58.163 に答える