【问题标题】:How to take a collection of bytes and pull typed values out of it?如何获取字节集合并从中提取类型值?
【发布时间】:2010-03-18 16:30:31
【问题描述】:

假设我有一个字节集合

var bytes = new byte[] {0, 1, 2, 3, 4, 5, 6, 7};

我想从字节中提取一个定义的值作为托管类型,例如ushort。有什么简单的方法可以定义哪些类型驻留在集合中的哪个位置并提取这些值?

一种(丑陋的)方法是使用带有索引的System.BitConverterQueuebyte[] 并简单地遍历,例如:

int index = 0;
ushort first = System.BitConverter.ToUint16(bytes, index);
index += 2; // size of a ushort
int second = System.BitConverter.ToInt32(bytes, index);
index += 4;
...

当您处理大量此类结构时,此方法会变得非常非常乏味!

我知道System.Runtime.InteropServices.StructLayoutAttribute 允许我在结构或类中定义类型的位置,但似乎没有办法将字节集合导入该结构。如果我能以某种方式将结构覆盖在字节集合上并提取值,那将是理想的。例如

Foo foo = (Foo)bytes; // doesn't work because I'd need to implement the implicit operator
ushort first = foo.first;
int second = foo.second;
...
[StructLayout(LayoutKind.Explicit, Size=FOO_SIZE)]
public struct Foo  {
    [FieldOffset(0)] public ushort first;
    [FieldOffset(2)] public int second;
}

关于如何实现这一点的任何想法?

[编辑:另见我的question on how to deal with the bytes when they are big endian。]

【问题讨论】:

  • 您感兴趣的所有类型都是积分(或“固定”积分数组)吗?
  • @gooch:不,但我们可以解决这个问题,并在我们有字符串时编写辅助方法。
  • 好的,这对于整数类型非常有效。对于这些类型的数组来说有点麻烦,因为它们需要被指定为“固定的”(这实际上意味着它是一个指向内存开头的指针)。我们无法实现其他类型,但是有一种方法可以为每个字段指定 Size,这很快就会变得很麻烦。

标签: .net interop binary legacy


【解决方案1】:

当我们通过串行字节直接与硬件通信时,我们已经做了很多。

给定结构定义

[StructLayout(LayoutKind.Explicit, Size=FOO_SIZE)]
public struct Foo  {
    [FieldOffset(0)] public ushort first;
    [FieldOffset(2)] public int second;
}

您可以使用这样的类来执行转换

public class ByteArrayToStruct<StructType>
{
    public StructType ConvertToStruct(int size, byte[] thebuffer)
    {
        try
        {
            int theSize = size;
            IntPtr ptr1 = Marshal.AllocHGlobal(theSize);
            Marshal.Copy(thebuffer, 0, ptr1, theSize);
            StructType theStruct = (StructType)Marshal.PtrToStructure(ptr1, typeof(StructType));
            Marshal.FreeHGlobal(ptr1);
            return theStruct;
        }
        catch (Exception)
        {
            return default(StructType);
        }
    }
}

相反,您也可以从数组创建 List 并执行以下操作:

ushort first = BitConverter.ToInt16(myList.ToArray(), 0);
myList.RemoveRange(0, sizeof(ushort));
[...]

这实际上是将相关数据保留在列表的“头部”,因此您不必跟踪数组中的位置。

【讨论】:

  • 不错!这就是我一直在寻找的,至少在我的初步测试中是这样。
  • 哦不,它只适用于小端字节(因为我的主机是小端)!你有一个解决方法来接收大端字节吗?请注意,反转整个字节数组是不够的,因为每个值都交换了字节序:例如从{1, 0, 2, 0, 0, 0},您可以拉出1 中的ushort2 中的uint,但是反转字节将为您提供0 中的ushort0x00020001 中的uint131073.
  • 基本上,我需要该结构在接收字节时表现得好像它是大端的,然后允许我从中获取值,就好像它是 .NET 的正常端托管代码。
  • 我明白你的意思。如果您不反对使用 3rd 方库,我发现了这个 SO 帖子:stackoverflow.com/questions/217980/…)
  • 我并不反对使用库,但 Jon 的代码并没有说明如何让结构体充当大端。他的代码只是将大端函数添加到 System.BitConverter(我已经在自己的库中完成了[太糟糕了,我之前找不到 Jon 的 MiscUtil 类!])。不过,感谢您的搜索。
【解决方案2】:

按照第一种方式。定义一个包含数据字节和游标的 Buffer 类。然后定义getInt16,getInt32等方法。然后你去

  Buffer b(bytes);
  ushort a = b.getInt16();
  int x = b.getInt32();

我的 utils 包里有这个。我也有相反的想法,用整数字符串做一个缓冲区,....

【讨论】:

    猜你喜欢
    • 2022-07-04
    • 1970-01-01
    • 2022-07-19
    • 2010-12-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-12-18
    相关资源
    最近更新 更多