2008-08-05 45 views
41

我正在嘗試使用C#讀取二進制數據。我擁有關於我想要讀取的文件中數據佈局的所有信息。我能夠讀取數據「塊大塊」,即獲得前40個字節的數據將其轉換爲字符串,獲得接下來的40個字節。將二進制文件讀入結構中

由於至少有三個稍微不同的版本的數據,我想直接讀取數據到一個結構。它只是比「逐行閱讀」更讓人感覺正確。

我曾嘗試以下方法,但都無濟於事:

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 n。

流包含的信息比我想要讀取的更多,因爲我對文件末尾的數據不感興趣。

的結構的定義如下:

[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 2009-07-26 07:11:52

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

也儘量

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

然後在您的BinaryReader在使用緩衝區[],而不是從的FileStream讀取數據,看看是否仍然可以AccessViolation例外。

我不得不使用 的BinaryFormatter沒有運氣,我想我必須 有一個完全匹配 文件的內容的完整結構。

這很有道理,BinaryFormatter有它自己的數據格式,完全不符合你的。

3

我沒有使用BinaryFormatter的運氣,我想我必須有一個完全匹配文件內容的結構。我意識到,最終我並沒有興趣十分文件內容的反正所以我讀流的一部分成字節緩衝區,然後將溶液去使用

Encoding.ASCII.GetString() 

絃樂和

將其轉換
BitConverter.ToInt32() 

整數。

我將需要能夠稍後解析更多的文件,但是對於這個版本,我只用了幾行代碼。

18

問題是您的結構中的字符串 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; 
} 
0

閱讀直入結構是邪惡的 - 許多C程序下降,因爲不同的字節序,場不同的編譯器實現,包裝,字長超過.......

您是逐字節串行化和反串行化的最佳選擇。如果您想要或使用BinaryReader,請使用build內容。

+6

我不同意,直接閱讀結構有時是將數據轉化爲可用對象的最快方式。如果你正在編寫面向性能的代碼,這可能非常有用。是的,你必須意識到路線和包裝,並確保任何端點機器將使用相同的。 – Joe 2012-02-03 20:00:30

+3

我也不同意。當性能是關鍵時,或者當您需要二進制C++/C#interop時,編寫普通的`struct`s是要走的路。 – 2012-03-25 07:48:04

5

正如Ronnie所說,我會使用BinaryReader並逐個讀取每個字段。我無法找到這篇文章的鏈接,但已經發現使用BinaryReader讀取每個單獨的字段可能比Marshal.PtrToStruct更快,如果結構包含少於30-40個字段的話。當我找到它時,我會將鏈接發佈到該文章。

文章的鏈接是:http://www.codeproject.com/Articles/10750/Fast-Binary-File-Reading-with-C

當編組結構的數組,PtrToStruct更迅速地獲得上層手,因爲你能想到的領域數作爲字段*數組的長度。

8

這是我正在使用的。
這成功地爲我讀取可移植的可執行格式。
這是一個通用功能,所以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; 
}