C# 将二进制文件读入结构体
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2384/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Read binary file into a struct
提问by Robert H?glund
I'm trying to read binary data using C#. I have all the information about the layout of the data in the files I want to read. I'm able to read the data "chunk by chunk", i.e. getting the first 40 bytes of data converting it to a string, get the next 40 bytes.
我正在尝试使用 C# 读取二进制数据。我有我想要阅读的文件中有关数据布局的所有信息。我能够“逐块”读取数据,即将前 40 个字节的数据转换为字符串,然后获取接下来的 40 个字节。
Since there are at least three slightly different version of the data, I would like to read the data directly into a struct. It just feels so much more right than by reading it "line by line".
由于数据至少有三个略有不同的版本,我想直接将数据读入结构体。感觉比“逐行”阅读要正确得多。
I have tried the following approach but to no avail:
我尝试了以下方法但无济于事:
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();
The stream is an opened FileStream from which I have began to read from. I get an AccessViolationExceptio
n when using Marshal.PtrToStructure
.
流是一个打开的 FileStream,我开始从中读取。我得到一个AccessViolationExceptio
使用当n Marshal.PtrToStructure
。
The stream contains more information than I'm trying to read since I'm not interested in data at the end of the file.
由于我对文件末尾的数据不感兴趣,因此该流包含的信息比我尝试读取的要多。
The struct is defined like:
结构体定义如下:
[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;
}
The examples code is changed from original to make this question shorter.
示例代码从原始更改为使这个问题更短。
How would I read binary data from a file into a struct?
如何将文件中的二进制数据读入结构体?
回答by urini
Try this:
尝试这个:
using (FileStream stream = new FileStream(fileName, FileMode.Open))
{
BinaryFormatter formatter = new BinaryFormatter();
StructType aStruct = (StructType)formatter.Deserialize(filestream);
}
回答by lubos hasko
I don't see any problem with your code.
我看不出你的代码有任何问题。
just out of my head, what if you try to do it manually? does it work?
就在我的脑海里,如果你尝试手动完成怎么办?它有效吗?
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));
...
...
...
also try
也试试
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();
then use buffer[]in your BinaryReader instead of reading data from FileStream to see whether you still get AccessViolation exception.
然后在您的 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 的运气,我想我必须有一个完全匹配文件内容的完整结构。
That makes sense, BinaryFormatter has its own data format, completely incompatible with yours.
这是有道理的,BinaryFormatter 有自己的数据格式,与您的完全不兼容。
回答by Robert H?glund
I had no luck using the BinaryFormatter, I guess I have to have a complete struct that matches the content of the file exactly. I realised that in the end I wasn't interested in very much of the file content anyway so I went with the solution of reading part of stream into a bytebuffer and then converting it using
我没有使用 BinaryFormatter 的运气,我想我必须有一个完全匹配文件内容的完整结构。我意识到最后我对很多文件内容都不感兴趣,所以我采用了将流的一部分读入字节缓冲区然后使用转换的解决方案
Encoding.ASCII.GetString()
for strings and
对于字符串和
BitConverter.ToInt32()
for the integers.
对于整数。
I will need to be able to parse more of the file later on but for this version I got away with just a couple of lines of code.
稍后我将需要能够解析更多文件,但对于这个版本,我只用了几行代码就可以了。
回答by Ishmaeel
The problem is the strings in your struct. I found that marshaling types like byte/short/int is not a problem; but when you need to marshal into a complex type such as a string, you need your struct to explicitly mimic an unmanaged type. You can do this with the MarshalAs attrib.
问题是结构中的字符串s 。我发现像 byte/short/int 这样的编组类型不是问题;但是当您需要编组为复杂类型(例如字符串)时,您需要您的结构显式模拟非托管类型。您可以使用 MarshalAs 属性执行此操作。
For your example, the following should work:
对于您的示例,以下内容应该有效:
[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;
}
回答by Ronnie
Reading straight into structs is evil - many a C program has fallen over because of different byte orderings, different compiler implementations of fields, packing, word size.......
直接读入结构是邪恶的——许多 C 程序因为不同的字节顺序、不同的字段编译器实现、打包、字长而失败......
You are best of serialising and deserialising byte by byte. Use the build in stuff if you want or just get used to BinaryReader.
您最擅长逐字节序列化和反序列化。如果您愿意或只是习惯了 BinaryReader,请使用内置内容。
回答by nevelis
As Ronnie said, I'd use BinaryReader and read each field individually. I can't find the link to the article with this info, but it's been observed that using BinaryReader to read each individual field can be faster than Marshal.PtrToStruct, if the struct contains less than 30-40 or so fields. I'll post the link to the article when I find it.
正如罗尼所说,我会使用 BinaryReader 并单独阅读每个字段。我找不到包含此信息的文章链接,但据观察,如果结构包含少于 30-40 个左右的字段,则使用 BinaryReader 读取每个单独的字段可能比 Marshal.PtrToStruct 更快。当我找到它时,我会发布该文章的链接。
The article's link is at: http://www.codeproject.com/Articles/10750/Fast-Binary-File-Reading-with-C
文章链接在:http: //www.codeproject.com/Articles/10750/Fast-Binary-File-Reading-with-C
When marshaling an array of structs, PtrToStruct gains the upper-hand more quickly, because you can think of the field count as fields * array length.
在编组结构数组时,PtrToStruct 会更快地占上风,因为您可以将字段计数视为字段 * 数组长度。
回答by nevelis
Here is what I am using.
This worked successfully for me for reading Portable Executable Format.
It's a generic function, so T
is your struct
type.
这是我正在使用的。
这对我阅读 Portable Executable Format 很成功。
这是一个通用函数,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;
}