2008-08-05 96 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; 
}