c# - 将二进制数据加载到结构中

标签 c#

我正在尝试使用从 byte[] 加载的数据填充结构(不必是实际结构)。

byte[]中有多种不同的数据结构,其中一种是字符串,声明为:

UInt16 stringLenght
byte[stringLenght] zeroTerminatedString

在“c”语言中,这可以通过声明固定大小的结构来处理,而不是包含实际字符串的结构,而是指向字符串的指针。

类似于:

UInt16 stringLength
char* zeroTerminatedString

在 C# 中是否有一种(聪明的)方法来做类似的事情?我的意思是从文件/内存加载二进制数据并将其填充到结构中?

问候 雅各布·贾斯特森

最佳答案

这不是你在 C 中声明它的方式。如果文件中的记录包含一个字符串,那么你将声明类似于以下的结构:

struct Example {
    int mumble;   // Anything, not necessarily a string length
    char text[42];
    // etc...
};

等效的 C# 声明如下所示:

    [StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)]
    private struct Example {
        public int mumble;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 42)]
        public string text;
        // etc...
    }

您通常会使用 BinaryReader 来读取数据。但它不能像这样直接处理字符串,你必须将它们作为 byte[] 读取并自己进行字符串转换。您也无法利用声明性语法,您必须为结构的每个单独成员编写一个调用。

有一个解决方法,Marshal 类已经知道如何使用 PtrToStructure() 方法将非托管结构转换为托管结构。这是一个通用实现,它适用于任何 blittable 类型。两个版本,一个从 byte[] 读取的静态版本和一个优化为从流中重复读取的实例方法。您可以将 FileStream 或 MemoryStream 与那个一起使用。

using System;
using System.IO;
using System.Runtime.InteropServices;

class StructTranslator {
    public static bool Read<T>(byte[] buffer, int index, ref T retval) {
        if (index == buffer.Length) return false;
        int size = Marshal.SizeOf(typeof(T));
        if (index + size > buffer.Length) throw new IndexOutOfRangeException();
        var handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
        try {
            IntPtr addr = (IntPtr)((long)handle.AddrOfPinnedObject() + index);
            retval = (T)Marshal.PtrToStructure(addr, typeof(T));
        }
        finally {
            handle.Free();
        }
        return true;
    }

    public bool Read<T>(Stream stream, ref T retval) {
        int size = Marshal.SizeOf(typeof(T));
        if (buffer == null || size > buffer.Length) buffer = new byte[size];
        int len = stream.Read(buffer, 0, size);
        if (len == 0) return false;
        if (len != size) throw new EndOfStreamException();
        var handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
        try {
            retval = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
        }
        finally {
            handle.Free();
        }
        return true;
    }

    private byte[] buffer;
}

未经测试,希望它有效。

关于c# - 将二进制数据加载到结构中,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58084869/

相关文章:

c# - 使用 Mailkit 访问 Exchange 共享文件夹

c# - Web 服务器在充当网关或代理服务器时收到无效响应

c# - 下载 URL 列表

c# - 如何检索属性关联对象的实例?

c# - 带有 Guid 的 MongoDB C# Upsert

C# 全局数组不起作用

javascript - 如何根据 url 或我们所在的页面在 _Layut 中呈现脚本

c# - 在 Windows 窗体中使用线程

c# - 如何在 Entity Framework 的 where 子句中链接替代条件

c# - 在 DirectInput 应用程序中使用 SendInput API 模拟键盘