c# -将二进制文件读入结构

Translate

我正在尝试使用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,我已从中开始读取该流。我得到一个AccessViolationException使用时Marshal.PtrToStructure.

由于我对文件末尾的数据不感兴趣,因此流中包含的信息比我尝试读取的信息多。

该结构的定义如下:

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

示例代码已从原始代码更改为简化此问题。

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

This question and all comments follow the "Attribution Required."

所有的回答

Translate

问题是在您的结构中。我发现诸如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;
}
来源
Bradley Lee
Translate

这是我正在使用的。
这对于我阅读可移植可执行格式来说是成功的。
这是一个通用功能,所以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;
}
来源
Translate

正如Ronnie所说,我将使用BinaryReader并分别读取每个字段。我找不到包含此信息的文章链接,但据观察,如果结构包含少于30-40个字段,使用BinaryReader读取每个单独的字段可能比Marshal.PtrToStruct更快。找到链接后,我将发布该文章的链接。

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

在整理结构数组时,PtrToStruct会更快地获得优势,因为您可以将字段数视为字段*数组长度。

来源
Translate

我没有使用BinaryFormatter的运气,我想我必须拥有一个完全匹配文件内容的完整结构。我意识到最终我对很多文件内容都不感兴趣,因此我采用了将一部分流读入字节缓冲区然后使用

Encoding.ASCII.GetString()

用于字符串和

BitConverter.ToInt32()

为整数。

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

来源
Translate

我看不到您的代码有任何问题。

就在我脑海中,如果您尝试手动执行该怎么办?它有效吗?

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具有自己的数据格式,与您的数据格式完全不兼容。

来源
Translate

尝试这个:

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

直接读入结构是邪恶的-许多C程序由于字节顺序不同,字段,打包,字长的不同编译器实现而崩溃。

您最好逐字节进行序列化和反序列化。如果需要,请使用内置的东西,或者只是习惯BinaryReader。

来源