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

时间:2010-10-05 11:27:32

标签: c#

我正在尝试填充一个结构(不一定是一个实际的结构),数据是从byte []加载的。

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

UInt16 stringLenght
byte[stringLenght] zeroTerminatedString

我是'语言'这可以通过声明一个固定大小的结构来处理,而不是包含实际字符串的结构,而是指向字符串。

类似的东西:

UInt16 stringLength
char* zeroTerminatedString

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

此致 Jakob Justesen

2 个答案:

答案 0 :(得分:4)

这不是你在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;
}

未经测试,希望它有效。

答案 1 :(得分:0)

Marshal应该可以为你做到这一点。

请注意,这只能对结构进行,您可能需要使用StructLayout属性。

我不是100%确定如何处理字符串或数组,但是BStrWrapperArrayWithOffset可能有帮助,也要留意类似的类/属性(我知道我已经完成了)这样的东西之前用于绑定到本机函数。)