将二进制文件读入结构
我正在尝试使用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。
由于我对文件末尾的数据不感兴趣,因此流中包含的信息比我尝试读取的信息多。
该结构的定义如下:
[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; }
示例代码已从原始代码更改为简化此问题。
我如何将二进制数据从文件读取到结构中?
解决方案
回答
试试这个:
using (FileStream stream = new FileStream(fileName, FileMode.Open)) { BinaryFormatter formatter = new BinaryFormatter(); StructType aStruct = (StructType)formatter.Deserialize(filestream); }
回答
我看不到代码有任何问题。
就在我脑海中,如果我们尝试手动执行该怎么办?它行得通吗?
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中读取数据,以查看是否仍然收到AccessViolation异常。
I had no luck using the BinaryFormatter, I guess I have to have a complete struct that matches the content of the file exactly.
这是有道理的,BinaryFormatter具有自己的数据格式,与数据格式完全不兼容。
回答
我没有使用BinaryFormatter的运气,我想我必须有一个完全匹配文件内容的完整结构。我意识到最后我对大部分文件内容都不感兴趣,因此我采取了将一部分流读取到字节缓冲区然后使用转换的解决方案。
Encoding.ASCII.GetString()
用于字符串和
BitConverter.ToInt32()
为整数。
稍后,我将需要能够解析更多文件,但是对于这个版本,我只用了几行代码。
回答
问题是结构中的字符串。我发现诸如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; }
回答
直接读入结构是很邪恶的,因为不同的字节顺序,字段,打包,字长的不同编译器实现,许多C程序已经崩溃了。
我们最好逐字节进行序列化和反序列化。如果需要,请使用内置的东西,或者只是习惯BinaryReader。
回答
正如Ronnie所说,我将使用BinaryReader并分别读取每个字段。我找不到包含此信息的文章链接,但是据观察,如果结构包含少于30-40个字段,使用BinaryReader读取每个单独的字段可能比Marshal.PtrToStruct更快。找到链接后,我将发布该文章的链接。
文章的链接位于:http://www.codeproject.com/Articles/10750/Fast-Binary-File-Reading-with-C
在整理结构数组时,PtrToStruct会更快地获得优势,因为我们可以将字段数视为字段*数组长度。
回答
这是我正在使用的。
对于阅读可移植可执行格式,这对我来说是成功的。
这是一个通用函数,因此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; }