将二进制文件读入结构

我正在尝试使用C#读取二进制数据。 我有关于我想要读取的文件中的数据布局的所有信息。 我能够读取数据“块大块”,即获得数据的前40个字节转换为一个string,获得下一个40个字节,…

由于至less有三个稍微不同的版本的数据,我想直接读取数据到一个结构。 它只是比“逐行阅读”更为合适。

我已经尝试了以下方法,但无济于事:

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

该stream是一个已经开始读取的已打开的FileStream。 当使用Marshal.PtrToStructure时,我得到一个AccessViolationException。

该stream包含更多的信息比我试图读取,因为我不感兴趣的数据在文件的末尾。

结构是这样定义的:

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

示例代码从原来的更改,以缩短这个问题。

如何将二进制数据从文件读取到结构中?

问题是你的结构中的string 。 我发现像byte / short / int这样的封送types不是问题; 但是当你需要编码成一个复杂types如string时,你需要你的结构来显式地模仿一个非托pipetypes。 你可以用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; } 

这是我正在使用的。
这为我成功读取可移植的可执行文件格式。
这是一个generics函数,所以T是你的structtypes。

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

正如罗尼所说,我会使用BinaryReader并逐个读取每个字段。 我找不到与这个信息的文章的链接,但已经观察到,使用BinaryReader读取每个单独的字段可以比Marshal.PtrToStruct更快,如果结构包含less于30-40左右的字段。 当我find它的时候,我会发布链接到文章。

文章链接在: http : //www.codeproject.com/Articles/10750/Fast-Binary-File-Reading-with-C

在编组结构数组时,PtrToStruct可以更快速地获得上手,因为您可以将字段数视为字段数组长度。

我没有使用BinaryFormatter的运气,我想我必须有一个完全匹配文件的内容完整的结构。 我意识到最后我对文件内容不是很感兴趣,所以我把读取部分stream的解决scheme放到一个bytebuffer中,然后使用它转换

 Encoding.ASCII.GetString() 

为string和

 BitConverter.ToInt32() 

为整数。

我将需要能够稍后parsing更多的文件,但是对于这个版本,我只用了几行代码。

我没有看到你的代码有任何问题。

只是在我的脑海中,如果你尝试手动做呢? 它工作吗?

 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中使用buffer []而不是从FileStream中读取数据来查看是否仍然存在AccessViolationexception。

我没有使用BinaryFormatter的运气,我想我必须有一个完全匹配文件的内容完整的结构。

这是有道理的,BinaryFormatter有自己的数据格式,完全不符合你的。

直接读入结构是邪恶的 – 许多C程序由于不同的字节顺序,字段的不同编译器实现,打包,字大小而下降了……

您是逐字节串行化和反串行化的最佳select。 如果你想要或者只是习惯BinaryReader的使用内置的东西。

尝试这个:

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