2008-08-05 43 views
41

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

少なくとも3つのバージョンのデータがあるため、データを構造体に直接読み込みたいと思います。それは "line by line"を読むことよりもはるかに正しいと感じます。

私は、次のアプローチを試みたが、無駄にしている:

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です。 Marshal.PtrToStructureを使用すると、AccessViolationExceptioが返されます。

ストリームには、ファイルの最後のデータには興味がないので、私が読もうとしているよりも多くの情報が含まれています。例のコードは、この質問を短くするために、オリジナルから変更され

[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; 
} 

構造体は、同じように定義されます。

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

答えて

0

はこれを試してみてください:

using (FileStream stream = new FileStream(fileName, FileMode.Open)) 
{ 
    BinaryFormatter formatter = new BinaryFormatter(); 
    StructType aStruct = (StructType)formatter.Deserialize(filestream); 
} 
+4

BinaryFormatterには、バイナリデータ用の独自のフォーマットがあります。データを自分で読み書きする場合は、これが適切です。他のソースからファイルを取得している場合は役に立ちません。 – russau

1

私はあなたのコードに問題が表示されません。

私の頭の中だけで、あなたが手動でやろうとしたらどうしますか?それは動作しますか?

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)); 
... 
... 
... 

もごBinaryReaderではなく、あなたはまだAccessViolation例外を取得するかどうかを確認するためのFileStreamからデータを読み取るバッファ[]を使用し、その後

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(); 

を試してみてください。

私は にBinaryFormatterを使用して運がなかった、私は に私が持っていると思い正確ファイルの 内容と一致する完全な構造体を持っています。

これは理にかなっています.BinaryFormatterには独自のデータ形式があり、完全に互換性がありません。

3

私はBinaryFormatterを使用して運がなかったので、ファイルの内容に完全に一致する完全な構造体を持っている必要があります。私は

Encoding.ASCII.GetString() 
文字列の

使用して、それを変換した後のByteBufferにストリームの一部を読み取り、の溶液と一緒に行ったので、私は最後に、私はとにかく、ファイルの内容のあまりに興味を持っていないことに気づきました
BitConverter.ToInt32() 

(整数)

後でファイルを解析できるようにする必要がありますが、このバージョンでは数行のコードで解決できます。

18

問題は文字列が構造体にあります。 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; 
} 
0

悪である - 多くのCプログラムが異なるため、バイト順序、フィールドの異なるコンパイラの実装、包装、ワードサイズのオーバー下落しています.......

バイト単位でシリアル化およびデシリアライズするのが最適です。 BinaryReaderを使いたい場合や、BinaryReaderを使い慣れている場合は、ビルドの中でビルドを使用してください。

+6

私は、構造体にまっすぐに読み込むことは、時にはデータを使用可能なオブジェクトに入れる最も速い方法であると考えています。あなたがパフォーマンス指向のコードを書いているなら、これは非常に役に立ちます。はい、アラインメントと梱包に気をつけ、どのエンドポイントマシンでも同じものを使用する必要があります。 – Joe

+3

私も同意しません。パフォーマンスが重要な場合、またはバイナリのC++/C#interopが必要な場合は、普通の 'struct'sを書くことが最善の方法です。 –

5

Ronnie氏によると、BinaryReaderを使用して各フィールドを個別に読み込みます。私はこの情報で記事へのリンクを見つけることができませんが、個々のフィールドを読み取るためにBinaryReaderを使用すると、構造体が30〜40フィールド未満の場合、Marshal.PtrToStructより速くなることが確認されています。記事を見つけたらリンクを投稿します。あなたがフィールド*配列の長さフィールドカウントと考えることができますので、http://www.codeproject.com/Articles/10750/Fast-Binary-File-Reading-with-C

構造体の配列をマーシャリング、PtrToStructは、より迅速に、上の手を得る:

記事のリンクがです。

+2

私はちょうど読んでいた:http://www.codeproject.com/KB/files/fastbinaryfileinput.aspx。これはあなたが考えている記事ですか?著者は次のように述べています。「約40のフィールドで、3つのアプローチの結果はほぼ同等であり、それを超えるとブロック読み取りアプローチが優位に立つことがわかりました。 –

+0

確かにそれです!良い見つける:) – nevelis

8

ここに私が使用しているものがあります。
これはPortable Executable Formatを読むための私にとってはうまくいきました。
これは汎用関数なので、Tstructタイプです。

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; 
} 
関連する問題