2011-08-02 20 views
1

私は処理が必要なこのリソースファイルを持っています。これは一連のファイルをパックします。バイトブロックを構造体に読み込む方法

struct FileEntry{ 
    byte Value1; 
    char Filename[12]; 
    byte Value2; 
    byte FileOffset[3]; 
    float whatever; 
} 

は、だから私は、まさにこのサイズのブロックを読み込む必要があります:

まず、リソースファイルには、この構造体のように、すべての中に含まれるファイルに加え、いくつかの他のデータを、一覧表示されます。

FileStreamのRead関数を使用していますが、構造体のサイズを指定するにはどうすればよいですか?

int sizeToRead = Marshal.SizeOf(typeof(Header)); 

をしてから読むためにこの値を渡し、その後私は、私はよく、私はどのように知っています(指定した値に変換する方法がわからないバイト[]のセットを読み取ることができます。 私が使用しました1バイトの値を取得しますが、残りのバイトは取得しません)。

また、私はバイトストリーム読み出しが、私は.NETで考えていたよりも厳しいですように私には思える

...私はそれが正しいですかどうか分からない危険なコンテキストを指定する必要があります:)

ありがとう!

+0

使用している言語を教えてください。私は野生の推測を行い、タグに[tag:c#]を追加しました。 –

+1

[AC#相当のCのfreadファイルi/o]と重複する可能性があります(http://stackoverflow.com/questions/1935851/ac-equivalent-of-cs-fread-file-io) –

+0

申し訳ありません、それは確かにC#。 –

答えて

7

これはC#であると仮定して、FileEntry型として構造体を作成しません。 char [20]を文字列に置き換え、BinaryReader - http://msdn.microsoft.com/en-us/library/system.io.binaryreader.aspxを使用して個々のフィールドを読み込みます。あなたは書かれたのと同じ順序でデータを読む必要があります。以下のような

何か:

class FileEntry { 
    byte Value1; 
    char[] Filename; 
    byte Value2; 
    byte[] FileOffset; 
    float whatever; 
} 

    using (var reader = new BinaryReader(File.OpenRead("path"))) { 
    var entry = new FileEntry { 
     Value1 = reader.ReadByte(), 
     Filename = reader.ReadChars(12) // would replace this with string 
     FileOffset = reader.ReadBytes(3), 
     whatever = reader.ReadFloat()   
    }; 
    } 

あなたが構造を持つと主張した場合、あなたはあなたの構造体は不変にし、あなたのフィールドのそれぞれの引数を持つコンストラクタを作成する必要があります。 BinaryReaderであなたのFileStreamラッピング

 

+0

これは魅力的に機能しました。どのように "これを文字列で置き換える"と思いますか? ReadString()を使用すると、サイズを指定することができないため、目的の位置を超えて読み込みます。 –

+0

実際には文字列のサイズは前に文字列として記述されていればストリームに含まれます。MSDNから - "現在のストリームから文字列を読み込みます。文字列には、一度に7ビットの整数としてエンコードされた長さが付加されています。" (http://msdn.microsoft.com/en-us/library/system.io.binaryreader.readstring.aspx)。ただし、その前にBinaryWriter.Write(文字列)も使用する必要があります。 chars - "StringField = new string(reader.ReadChars(20));"を使用して文字列を作成できます。 – Vasea

+0

今、愚かな気分になりました。皆さん、ありがとうございました! –

2

はあなたを与えるだろうプリミティブ型のRead*()方法捧げ:私の頭のうち http://msdn.microsoft.com/en-us/library/system.io.binaryreader.aspx

を、あなたには、適切な表現を確実にするために([StructLayout(LayoutKind.Sequential)]であなたのstructをおそらくマーク可能性メモリ)と実際に構造体Cスタイルを埋めるためにunsafeブロック内のポインタを使用します。ただし、実際には必要ない場合(interop、画像処理などの重い操作)はunsafeにすることはお勧めしません。

5

あなたは危険なコードを使用することができた場合:固定キーワードは、構造体の配列を埋め込む

unsafe struct FileEntry{ 
    byte Value1; 
    fixed char Filename[12]; 
    byte Value2; 
    fixed byte FileOffset[3]; 
    float whatever; 
} 

public unsafe FileEntry Get(byte[] src) 
{ 
    fixed(byte* pb = &src[0]) 
    { 
     return *(FileEntry*)pb; 
    } 
} 

を。固定されているので、このにはがあります。これらは常に作成していて、決して放置しないとGCの問題を引き起こします。一定のサイズはn * sizeof(t)であることに注意してください。したがって、ファイル名[12]は24バイト(各文字は2バイトのユニコード)を割り当てており、FileOffset [3]は3バイトを割り当てています。これは、ディスク上のUnicodeデータを扱っていない場合に重要です。私はそれをバイト[]に変更し、文字列を変換できる使用可能なクラスに構造体を変換することをお勧めします。

あなたが危険なを使用できない場合、あなたは全体のBinaryReaderアプローチ行うことができます。

public unsafe FileEntry Get(Stream src) 
{ 
    FileEntry fe = new FileEntry(); 
    var br = new BinaryReader(src); 
    fe.Value1 = br.ReadByte(); 
    ... 
} 

を危険な方法は、あなたが一度に構造体の多くを変換している場合は特に、はるかに速く、ほぼ瞬時です。問題は、安全でないものを使用したいかどうかです。 が絶対に必要な場合は、安全性の高い方法を使用することをお勧めします。パフォーマンスが向上します。

+0

おそらく、より安全でない方が安全ではないでしょう。なぜなら、それは、処理速度が本当に目立たない小さなファイルだからです。 –

3

ベースthis articleですが、これを一般的なものにしました。これは、データを構造体に直接マーシャリングする方法です。より長いデータ型には非常に便利です。

public static T RawDataToObject<T>(byte[] rawData) where T : struct 
{ 
    var pinnedRawData = GCHandle.Alloc(rawData, 
             GCHandleType.Pinned); 
    try 
    { 
     // Get the address of the data array 
     var pinnedRawDataPtr = pinnedRawData.AddrOfPinnedObject(); 

     // overlay the data type on top of the raw data 
     return (T) Marshal.PtrToStructure(pinnedRawDataPtr, typeof(T)); 
    } 
    finally 
    { 
     // must explicitly release 
     pinnedRawData.Free(); 
    } 
} 

使用例:

[StructLayout(LayoutKind.Sequential)] 
public struct FileEntry 
{ 
    public readonly byte Value1; 

    //you may need to play around with this one 
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)] 
    public readonly string Filename; 

    public readonly byte Value2; 

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] 
    public readonly byte[] FileOffset; 

    public readonly float whatever; 
} 

private static void Main(string[] args) 
{ 
    byte[] data =;//from file stream or whatever; 
    //usage 
    FileEntry entry = RawDataToObject<FileEntry>(data); 
} 
+0

ええ、文字列はおそらく 'Byte []'として読みやすくなり、 'Char []'に変換してから 'String'に変換するいくつかのメソッドを使用して取得します。いくつかの初歩的な "ascii-only"チェックと古典的なend-on-0 C文字列の動作では、 'String filename = new String(filenameArr.TakeWhile(x => x!= 0).Select(x => x <128?Convert.ToChar(x): '?')。ToArray()); ' – Nyerguds

0

ない完全な答え(それがカバーされています私は思う)が、ファイル名に特定のノート:

Charタイプは、おそらくワンではありません.Net文字はUnicodeなので、255文字を超える文字値をサポートしているので、ファイル名のデータをChar[]配列として解釈すると問題が発生します。だから、最初のステップは間違いなくByte[12]で、それはChar[12]ではありません。

アレイをcharへのバイト配列からストレート変換はまた、このような二進指数、おそらく0バイトでパディングする許可12文字より短いであるファイル名、そうストレート変換であるため、しかし、お勧めされていません常に12文字の長さの文字列になり、これらのゼロ文字で終了する可能性があります。そのようなデータのための読書システムは、通常、単に最初のゼロが発生し、書き込みシステムdoesnの場合は、配列で実際にゴミが含まれている可能性があることをの背後にあるデータはまで読んしかし、単にこれらのゼロをオフにトリミングが、お勧めできません

それに文字列を入れる前に、バッファをゼロできれいにするのは面倒です。それは、読書システムが文字列を最初のゼロまでしか解釈しないと仮定しているので、多くのプログラムがやっていないことです。

これは実際には、ASCIIまたはWin-1252のような1バイトあたり1バイトのテキストエンコーディングで保存される典型的なゼロ終了(Cスタイル)の文字列であると仮定すると、最初のゼロの文字列をオフにします。あなたはLinqのTakeWhile機能でこれを簡単に行うことができます。そして、3番目と最後のステップは、それがで書かれている1バイトあたりの文字のテキストエンコーディングがあることを起こるものは何でもして文字列に結果のバイト配列を変換することです:

public String StringFromCStringArray(Byte[] readData, Encoding encoding) 
{ 
    return encoding.GetString(readData.TakeWhile(x => x != 0).ToArray()); 
} 

私が言ったように、エンコードがでしょうで取得できる純粋なASCIIのようなものです.またはWindows-1252の米国/西ヨーロッパ標準のWindowsテキストエンコーディングからアクセスできます。

関連する問題