एक बाइट सरणी से C # में C / C ++ डेटा संरचना पढ़ना


85

एक बाइट से [#] एरे जहां डेटा सी / सी ++ स्ट्रक्चर से था, वहां भरने के लिए सबसे अच्छा तरीका क्या होगा? सी संरचना कुछ इस तरह दिखती है (मेरी सी बहुत जंग लगी है):

typedef OldStuff {
    CHAR Name[8];
    UInt32 User;
    CHAR Location[8];
    UInt32 TimeStamp;
    UInt32 Sequence;
    CHAR Tracking[16];
    CHAR Filler[12];
}

और कुछ इस तरह भरेंगे:

[StructLayout(LayoutKind.Explicit, Size = 56, Pack = 1)]
public struct NewStuff
{
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 8)]
    [FieldOffset(0)]
    public string Name;

    [MarshalAs(UnmanagedType.U4)]
    [FieldOffset(8)]
    public uint User;

    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 8)]
    [FieldOffset(12)]
    public string Location;

    [MarshalAs(UnmanagedType.U4)]
    [FieldOffset(20)]
    public uint TimeStamp;

    [MarshalAs(UnmanagedType.U4)]
    [FieldOffset(24)]
    public uint Sequence;

    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 16)]
    [FieldOffset(28)]
    public string Tracking;
}

नकल OldStuffकरने का सबसे अच्छा तरीका क्या है NewStuff, यदि OldStuffबाइट [] सरणी के रूप में पारित किया गया था?

मैं वर्तमान में निम्नलिखित की तरह कुछ कर रहा हूँ, लेकिन यह क्लूनी की तरह लगता है।

GCHandle handle;
NewStuff MyStuff;

int BufferSize = Marshal.SizeOf(typeof(NewStuff));
byte[] buff = new byte[BufferSize];

Array.Copy(SomeByteArray, 0, buff, 0, BufferSize);

handle = GCHandle.Alloc(buff, GCHandleType.Pinned);

MyStuff = (NewStuff)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(NewStuff));

handle.Free();

क्या इसे पूरा करने का कोई बेहतर तरीका है?


क्या BinaryReaderकक्षा का उपयोग मेमोरी को पिन करने और उपयोग करने पर कोई प्रदर्शन लाभ प्रदान करेगा Marshal.PtrStructure?


1
FYI करें, यदि आपका प्रोग्राम विभिन्न मशीनों पर चलता है, तो आपको छोटे बनाम बड़े एंडियन को संभालने की आवश्यकता हो सकती है।
केपेक्सईए

1
आप संरचना में प्रत्येक मूल्य के लिए बाइट्स को व्यक्तिगत रूप से उलट किए बिना, संरचना के स्तर पर कैसे संभाल सकते हैं?
पैट

जवाबों:


114

उस संदर्भ में मैं क्या देख सकता हूं, आपको SomeByteArrayबफर में कॉपी करने की आवश्यकता नहीं है । आपको बस से हैंडल प्राप्त करने की आवश्यकता है SomeByteArray, इसे पिन करें, IntPtrउपयोग करने वाले डेटा की प्रतिलिपि बनाएँ PtrToStructureऔर फिर रिलीज़ करें। कॉपी की जरूरत नहीं।

यह होगा:

NewStuff ByteArrayToNewStuff(byte[] bytes)
{
    GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
    try
    {
        NewStuff stuff = (NewStuff)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(NewStuff));
    }
    finally
    {
        handle.Free();
    }
    return stuff;
}

सामान्य संस्करण:

T ByteArrayToStructure<T>(byte[] bytes) where T: struct 
{
    T stuff;
    GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
    try
    {
        stuff = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
    }
    finally
    {
        handle.Free();
    }
    return stuff;
}

सरल संस्करण ( unsafeस्विच की आवश्यकता है ):

unsafe T ByteArrayToStructure<T>(byte[] bytes) where T : struct
{
    fixed (byte* ptr = &bytes[0])
    {
        return (T)Marshal.PtrToStructure((IntPtr)ptr, typeof(T));
    }
}

CS0411 विधि 'ByteArrayToStructure <T> (बाइट [], int)' के प्रकार तर्क का उपयोग से अनुमान नहीं लगाया जा सकता है। प्रकार तर्कों को स्पष्ट रूप से निर्दिष्ट करने का प्रयास करें। (मैंने बाइट सरणी का इंट इंडेक्स जोड़ा)।
एसएसपोक

अपवादों की उपस्थिति में स्मृति को रिसाव करेगा। देखें: एक सुरक्षित संस्करण के लिए stackoverflow.com/a/41836532/184528
सीडीगिन्स

2
4.5.1 तक, PtrToStructure का एक सामान्य संस्करण है, इसलिए सामान्य संस्करण में दूसरी पंक्ति, ऊपर, बन सकती है: var stuff = Marshal.PtrToStructure<T>(handle.AddrOfPinnedObject());

10

यहां स्वीकृत उत्तर का एक अपवाद सुरक्षित संस्करण है :

public static T ByteArrayToStructure<T>(byte[] bytes) where T : struct
{
    var handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
    try {
        return (T) Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
    }
    finally {
        handle.Free();
    }
}

3
@ बेन-कॉलिन्स स्वीकृत उत्तर संपादित करने के बाद मैंने अपना उत्तर जोड़ा।
cdiggins

5

पैकिंग मुद्दों के लिए बाहर देखो। उदाहरण में आपने दिया कि सभी क्षेत्र स्पष्ट स्थिति में हैं क्योंकि सब कुछ 4 बाइट सीमाओं पर है, लेकिन यह हमेशा ऐसा नहीं होगा। डिफ़ॉल्ट रूप से 8 बाइट सीमाओं पर दृश्य C ++ पैक।


1
"विज़ुअल सी ++ डिफ़ॉल्ट रूप से 8 बाइट सीमाओं पर पैक करता है ।" इससे मेरी समस्या हल हो गई, बहुत बहुत धन्यवाद!
क्रिस एल

4
object ByteArrayToStructure(byte[] bytearray, object structureObj, int position)
{
    int length = Marshal.SizeOf(structureObj);
    IntPtr ptr = Marshal.AllocHGlobal(length);
    Marshal.Copy(bytearray, 0, ptr, length);
    structureObj = Marshal.PtrToStructure(Marshal.UnsafeAddrOfPinnedArrayElement(bytearray, position), structureObj.GetType());
    Marshal.FreeHGlobal(ptr);
    return structureObj;
}   

ये लो


0

यदि आपके पास एक बाइट है [तो आपको बाइनरीरएडर क्लास का उपयोग करने में सक्षम होना चाहिए और उपलब्ध एक्सएक्सएक्स विधियों का उपयोग करके न्यूस्टफ पर मान सेट करना चाहिए।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.