使用MarshalAs和StructLayout读取二进制数据

时间:2009-12-11 11:46:23

标签: c# .net

我坚持这个。

我正在阅读一个二进制文件,其格式如下:

Field name  Size in bytes     Example
-------------------------------------
Date        19                1998_12_22 PM 20:15
Serial      4                 0001

使用以下结构并使用this question的答案我正在尝试阅读该文件。

[StructLayout(LayoutKind.Explicit, Size=23, Pack = 1)]
struct MeasurementStruct
{
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 19)]
    [FieldOffset(0)]
    public string Date;

    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 4)]
    [FieldOffset(19)]
    public string Serial;
}

然而,当实例化保存此结构的类时,我在FieldOffset 19处收到错误。不幸的是,此错误是荷兰语,但它大致转换为“无法加载MeasurementStruct,因为19号关闭的字段未对齐(字段可能重叠) )”。

我发现将FieldOffset [19]更改为FieldOffset [20]会使错误消失。但是,在我的情况下,20不是正确的偏移,是吗?

4 个答案:

答案 0 :(得分:1)

您的数据(23个字节)和结构大小(24个字节)之间似乎存在大小问题。这是正常的吗?

更新:Pack = 1属性应保证内存对齐,但谷歌搜索会对结构的字段对齐产生不同的响应 。 你可以做的是读取一个字节数组中的23个字节,然后使用Encoding类提取两个字符串:

byte[] array = ... // read 23 bytes
String s1 = Encoding.ASCII.GetString(array, 0, 19);
String s2 = Encoding.ASCII.GetString(array, 19, 4); 

答案 1 :(得分:1)

没有一个答案正是我正在寻找的,但我通过定义结构来找到一个更好的方法。

[StructLayout(LayoutKind.Sequential, Size = MeasurementStructSize, Pack = 1)]
struct MeasurementStruct
{
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 19)]
    public string Date;

    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 4)]
    public string SerialNumber;

}

我将LayoutKind更改为Sequential。在这种情况下,Pack字段实际上有意义,我不需要添加FieldOffsets。以上工作正常。

非常感谢大家!

答案 2 :(得分:0)

20可能是此架构的正确偏移量。编译器将使用零填充以将struct成员放在偶数位置。确切的“多么均匀”是架构和编译器的依赖,但如果我没有弄错的话,它必须是32位架构中4的倍数。

例如,以下结构:

struct MyStruct
{
  char ch;  // Offset 0
            // 3 "invisible" bytes padding inserted by the compiler
  int i;    // Offset 4
}

答案 3 :(得分:0)

可能是string每个字符需要2个字节(unicode),读取为字节数组并使用ASCIIEncoding转换为字符串