59

C# を使用してバイナリ データを読み込もうとしています。読みたいファイル内のデータのレイアウトに関するすべての情報を持っています。データを「チャンクごと」に読み取ることができます。つまり、データの最初の 40 バイトを取得して文字列に変換し、次の 40 バイトを取得します。

わずかに異なるバージョンのデータが少なくとも 3 つあるため、データを直接構造体に読み込みたいと考えています。「一行ずつ」読むよりも、はるかに正しいと感じます。

私は次のアプローチを試みましたが、役に立ちませんでした:

StructType aStruct;
int count = Marshal.SizeOf(typeof(StructType));
byte[] readBuffer = new byte[count];
BinaryReader reader = new BinaryReader(stream);
readBuffer = reader.ReadBytes(count);
GCHandle handle = GCHandle.Alloc(readBuffer, GCHandleType.Pinned);
aStruct = (StructType) Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(StructType));
handle.Free();

ストリームは、私が読み取りを開始した、開いている FileStream です。AccessViolationExceptioを使用すると nが得られMarshal.PtrToStructureます。

ファイルの末尾にあるデータには関心がないため、ストリームには読み込もうとしているよりも多くの情報が含まれています。

構造体は次のように定義されます。

[StructLayout(LayoutKind.Explicit)]
struct StructType
{
    [FieldOffset(0)]
    public string FileDate;
    [FieldOffset(8)]
    public string FileTime;
    [FieldOffset(16)]
    public int Id1;
    [FieldOffset(20)]
    public string Id2;
}

この質問を短くするために、サンプルコードは元のコードから変更されています。

ファイルから構造体にバイナリデータを読み込むにはどうすればよいですか?

4

7 に答える 7

33

問題は、構造体の文字列s です。byte/short/int などの型のマーシャリングは問題ではないことがわかりました。ただし、文字列などの複雑な型にマーシャリングする必要がある場合は、アンマネージ型を明示的に模倣する構造体が必要です。これは MarshalAs 属性で行うことができます。

あなたの例では、次のように動作するはずです。

[StructLayout(LayoutKind.Explicit)]
struct StructType
{
    [FieldOffset(0)]
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 8)]
    public string FileDate;

    [FieldOffset(8)]
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 8)]
    public string FileTime;

    [FieldOffset(16)]
    public int Id1;

    [FieldOffset(20)]
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 66)] //Or however long Id2 is.
    public string Id2;
}
于 2008-08-21T19:02:50.507 に答える
16

これが私が使用しているものです。
これは、Portable Executable Format の読み取りに成功しました。
これはジェネリック関数なので、Tあなたのstructタイプです。

public static T ByteToType<T>(BinaryReader reader)
{
    byte[] bytes = reader.ReadBytes(Marshal.SizeOf(typeof(T)));

    GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
    T theStructure = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
    handle.Free();

    return theStructure;
}
于 2010-11-02T02:40:14.273 に答える
6

Ronnieが言ったように、私はBinaryReaderを使用して、各フィールドを個別に読み取ります。この情報を含む記事へのリンクは見つかりませんが、構造体に含まれるフィールドが30〜40程度未満の場合、BinaryReaderを使用して個々のフィールドを読み取る方がMarshal.PtrToStructよりも高速であることが確認されています。記事を見つけたら、その記事へのリンクを投稿します。

記事のリンクは次の場所にあります:http://www.codeproject.com/Articles/10750/Fast-Binary-File-Reading-with-C

構造体の配列をマーシャリングする場合、フィールド数をフィールド*配列の長さと考えることができるため、PtrToStructはより迅速に優位に立つことができます。

于 2010-05-06T01:04:40.047 に答える
3

あなたのコードに問題はありません。

頭から離れましたが、手動でやろうとするとどうなりますか? それは機能しますか?

BinaryReader reader = new BinaryReader(stream);
StructType o = new StructType();
o.FileDate = Encoding.ASCII.GetString(reader.ReadBytes(8));
o.FileTime = Encoding.ASCII.GetString(reader.ReadBytes(8));
...
...
...

も試してください

StructType o = new StructType();
byte[] buffer = new byte[Marshal.SizeOf(typeof(StructType))];
GCHandle handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
Marshal.StructureToPtr(o, handle.AddrOfPinnedObject(), false);
handle.Free();

次に、FileStream からデータを読み取る代わりに BinaryReader でbuffer[]を使用して、引き続き AccessViolation 例外が発生するかどうかを確認します。

BinaryFormatter を使用することはできませんでした。ファイルの内容と正確に一致する完全な構造体が必要だと思います。

BinaryFormatter には独自のデータ形式があり、あなたのデータ形式とは完全に互換性がありません。

于 2008-08-05T15:31:43.330 に答える
3

BinaryFormatter を使用することはできませんでした。ファイルの内容と正確に一致する完全な構造体が必要だと思います。結局、ファイルの内容にはあまり興味がないことに気付いたので、ストリームの一部をバイトバッファに読み込んでから、

Encoding.ASCII.GetString()

文字列と

BitConverter.ToInt32()

整数の場合。

後でファイルをさらに解析できるようにする必要がありますが、このバージョンでは、数行のコードだけで済みました。

于 2008-08-06T09:03:28.277 に答える
0

これを試して:

using (FileStream stream = new FileStream(fileName, FileMode.Open))
{
    BinaryFormatter formatter = new BinaryFormatter();
    StructType aStruct = (StructType)formatter.Deserialize(filestream);
}
于 2008-08-05T14:56:10.937 に答える
0

構造体を直接読み取るのは悪いことです。多くの C プログラムは、バイト順の違い、フィールドのコンパイラの実装の違い、パッキング、ワード サイズの違いにより失敗しました。

バイトごとにシリアル化および逆シリアル化するのが最善です。必要に応じてビルドを使用するか、BinaryReader に慣れてください。

于 2008-09-23T21:43:29.523 に答える