2010-10-05 63 views
0

我想填充一个结构(不一定是一个实际的结构),与从一个字节[]加载数据。加载二进制数据到一个结构

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

UInt16 stringLenght 
byte[stringLenght] zeroTerminatedString 

I“C”语言这可以通过声明一个固定尺寸结构来处理,而不是包含实际字符串的结构,请指向该字符串。

喜欢的东西:

UInt16 stringLength 
char* zeroTerminatedString 

是否有(智能)的方式做在C#中类似的东西?我的意思是从文件/内存加载二进制数据并将其填充到结构中?

问候 雅各布Justesen

+2

您提出的C代码有问题,因此您将其转换为C#的机会注定要失败! – 2010-10-05 11:32:47

+0

这取决于。多少条数据记录?他们多大? – 2010-10-05 11:34:44

回答

0

Marshal应该能够为你做这个。

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

我不是100%确定如何处理字符串或数组,但BStrWrapperArrayWithOffset可能的帮助,也要留意寻找类似的类/属性(我知道我已经完成了像这样的东西来绑定到本地功能)。

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; 
} 

未经测试,希望它能正常工作。