将二进制文件读入结构

我正在尝试使用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
额外 编辑
意见: 2

6 答案

我没有使用BinaryFormatter的运气,我想我必须有一个完全匹配文件内容的完整结构。我意识到最终我对文件内容不感兴趣,所以我将解读流的一部分读入bytebuffer,然后使用它转换

Encoding.ASCII.GetString()

为字符串和

BitConverter.ToInt32()

整数。

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

0
额外

直接阅读结构是邪恶的 - 许多C程序因字节顺序不同,字段的不同编译器实现,打包,字大小等原因而倒下了。

您是逐字节串行化和反串行化的最佳选择。如果您想要或使用BinaryReader,请使用build内容。

0
额外
我也不同意。当性能是关键时,或者当您需要二进制C ++ / C#interop时,编写普通的 struct 是最好的方法。
额外 作者 Dmitri Nesteruk,
我不同意,直接阅读结构有时是将数据转化为可用对象的最快方式。如果你正在编写面向性能的代码,这可能非常有用。是的,你必须意识到路线和包装,并确保任何端点机器将使用相同的。
额外 作者 Joe,

尝试这个:

using (FileStream stream = new FileStream(fileName, FileMode.Open))
{
    BinaryFormatter formatter = new BinaryFormatter();
    StructType aStruct = (StructType)formatter.Deserialize(filestream);
}
0
额外
BinaryFormatter拥有自己的二进制数据格式 - 如果您自己读取/写入数据,这很好。如果您从其他来源获取文件则无用。
额外 作者 russau,

问题是你的结构中的字符串。我发现像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
额外

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

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

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异常。

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

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

0
额外

正如Ronnie所说,我会使用BinaryReader并分别读取每个字段。我无法找到这篇文章的链接,但已经发现使用BinaryReader读取每个单独的字段可能比Marshal.PtrToStruct更快,如果结构包含少于30-40个字段的话。当我找到它时,我会将链接发布到文章。

The article's link is at: http://www.codeproject.com/Articles/10750/Fast-Binary-File-Reading-with-C

编组结构数组时,PtrToStruct可以更快地获得上风,因为您可以将字段计数看作字段*数组长度。

0
额外
它的确是!好发现:)
额外 作者 nevelis,
我只是在阅读: codeproject.com/KB/files/fastbinaryfileinput.aspx 。这是你正在想的文章吗?作者指出:“我发现,在大约40个领域,这三种方法的结果几乎相当,而且在这之外,块阅读方法占了上风。”
额外 作者 Neal Stublen,