2015-01-29 9 views
5

मैं एक अनुभवी पायथन डेवलपर हूं और इसकी कई सुविधाएं पसंद करने आया हूं। मैंने वास्तव में कुछ समय के लिए सी # को जाना है लेकिन हाल ही में कुछ और उन्नत कोडिंग में शामिल हो गए हैं।पाइथन के "struct.pack/unpack" के सी # में समतुल्य?

मुझे क्या आश्चर्य है कि अगर सी # में एक बाइट सरणी को "अलग-अलग आकार के आइटमों" में सेट करने का कोई तरीका है।

कल्पना कीजिए कि हम इस राशि:

सी #:

import struct 
byteArray = "\xFF\xFF\x00\x00\x00\xFF\x01\x00\x00\x00" 
numbers = struct.unpack("<LHL",byteArray) 
print numbers[0] # 65535 
print numbers[1] # 255 
print numbers[2] # 1 

newNumbers = [0, 255, 1023] 
byteArray = struct.pack("<HHL",newNumbers) 
print byteArray # '\x00\x00\xFF\x00\xFF\x03\x00\x00' 

मैं सी # में एक ही प्रभाव हासिल करना चाहते हैं, इस तरह कोड की भारी, गंदा मात्रा का सहारा के बिना:

पायथन:

byte[] byteArray = new byte[] { 255, 255, 0, 0, 0, 255, 1, 0, 0, 0 }; 
byte[] temp; 

int[] values = new int[3]; 

temp = new byte[4]; 
Array.Copy(byteArray, 0, temp, 0, 4); 
values[0] = BitConverter.ToInt32(temp); 

temp = new byte[2]; 
Array.Copy(byteArray, 4, temp, 0, 2); 
values[1] = BitConverter.ToInt16(temp); 

temp = new byte[4]; 
Array.Copy(byteArray, 8, temp, 0, 4); 
values[2] = BitConverter.ToInt32(temp); 

// Now values contains an array of integer values. 
// It would be OK to assume a common maximum (e.g. Int64) and just cast up to that, 
// but we still have to consider the size of the source bytes. 

// Now the other way. 
int[] values = new int[] { 0, 255, 1023 }; 
byteArray = new byte[8]; 

temp = BitConverter.GetBytes(values[0]); 
Array.Copy(temp,2,byteArray,0,2); 

temp = BitConverter.GetBytes(values[1]); 
Array.Copy(temp,2,byteArray,2,2); 

temp = BitConverter.GetBytes(values[2]); 
Array.Copy(temp,0,byteArray,4,4); 

जाहिर है मैं सी # कोड बहुत है विशिष्ट और किसी भी तरह से वास्तव में पुन: प्रयोज्य नहीं है।

सलाह?

+0

मैं विभिन्न तकनीकों के प्रदर्शन के बारे में भी उत्सुक हूं। – gbronner

उत्तर

5

मैंने इसे संभालने के लिए अपनी कक्षा लिखना समाप्त कर दिया। यह बहुत जटिल है, लेकिन ऐसा लगता है कि यह काम करता है। यह अधूरा भी है, लेकिन यह इस बिंदु पर मुझे जो चाहिए वह काम करता है। इसका उपयोग करने के लिए स्वतंत्र महसूस करें, और यदि कोई अच्छा सुधार हुआ है, तो कृपया मुझे बताएं।

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Diagnostics; 

// This is a crude implementation of a format string based struct converter for C#. 
// This is probably not the best implementation, the fastest implementation, the most bug-proof implementation, or even the most functional implementation. 
// It's provided as-is for free. Enjoy. 

public class StructConverter 
{ 
    // We use this function to provide an easier way to type-agnostically call the GetBytes method of the BitConverter class. 
    // This means we can have much cleaner code below. 
    private static byte[] TypeAgnosticGetBytes(object o) 
    { 
     if (o is int) return BitConverter.GetBytes((int)o); 
     if (o is uint) return BitConverter.GetBytes((uint)o); 
     if (o is long) return BitConverter.GetBytes((long)o); 
     if (o is ulong) return BitConverter.GetBytes((ulong)o); 
     if (o is short) return BitConverter.GetBytes((short)o); 
     if (o is ushort) return BitConverter.GetBytes((ushort)o); 
     if (o is byte || o is sbyte) return new byte[] { (byte)o }; 
     throw new ArgumentException("Unsupported object type found"); 
    } 

    private static string GetFormatSpecifierFor(object o) 
    { 
     if (o is int) return "i"; 
     if (o is uint) return "I"; 
     if (o is long) return "q"; 
     if (o is ulong) return "Q"; 
     if (o is short) return "h"; 
     if (o is ushort) return "H"; 
     if (o is byte) return "B"; 
     if (o is sbyte) return "b"; 
     throw new ArgumentException("Unsupported object type found"); 
    } 

    /// <summary> 
    /// Convert a byte array into an array of objects based on Python's "struct.unpack" protocol. 
    /// </summary> 
    /// <param name="fmt">A "struct.pack"-compatible format string</param> 
    /// <param name="bytes">An array of bytes to convert to objects</param> 
    /// <returns>Array of objects.</returns> 
    /// <remarks>You are responsible for casting the objects in the array back to their proper types.</remarks> 
    public static object[] Unpack(string fmt, byte[] bytes) 
    { 
     Debug.WriteLine("Format string is length {0}, {1} bytes provided.", fmt.Length, bytes.Length); 

     // First we parse the format string to make sure it's proper. 
     if (fmt.Length < 1) throw new ArgumentException("Format string cannot be empty."); 

     bool endianFlip = false; 
     if (fmt.Substring(0, 1) == "<") 
     { 
      Debug.WriteLine(" Endian marker found: little endian"); 
      // Little endian. 
      // Do we need to flip endianness? 
      if (BitConverter.IsLittleEndian == false) endianFlip = true; 
      fmt = fmt.Substring(1); 
     } 
     else if (fmt.Substring(0, 1) == ">") 
     { 
      Debug.WriteLine(" Endian marker found: big endian"); 
      // Big endian. 
      // Do we need to flip endianness? 
      if (BitConverter.IsLittleEndian == true) endianFlip = true; 
      fmt = fmt.Substring(1); 
     } 

     // Now, we find out how long the byte array needs to be 
     int totalByteLength = 0; 
     foreach (char c in fmt.ToCharArray()) 
     { 
      Debug.WriteLine(" Format character found: {0}", c); 
      switch (c) 
      { 
       case 'q': 
       case 'Q': 
        totalByteLength += 8; 
        break; 
       case 'i': 
       case 'I': 
        totalByteLength += 4; 
        break; 
       case 'h': 
       case 'H': 
        totalByteLength += 2; 
        break; 
       case 'b': 
       case 'B': 
       case 'x': 
        totalByteLength += 1; 
        break; 
       default: 
        throw new ArgumentException("Invalid character found in format string."); 
      } 
     } 

     Debug.WriteLine("Endianness will {0}be flipped.", (object) (endianFlip == true ? "" : "NOT ")); 
     Debug.WriteLine("The byte array is expected to be {0} bytes long.", totalByteLength); 

     // Test the byte array length to see if it contains as many bytes as is needed for the string. 
     if (bytes.Length != totalByteLength) throw new ArgumentException("The number of bytes provided does not match the total length of the format string."); 

     // Ok, we can go ahead and start parsing bytes! 
     int byteArrayPosition = 0; 
     List<object> outputList = new List<object>(); 
     byte[] buf; 

     Debug.WriteLine("Processing byte array..."); 
     foreach (char c in fmt.ToCharArray()) 
     { 
      switch (c) 
      { 
       case 'q': 
        outputList.Add((object)(long)BitConverter.ToInt64(bytes,byteArrayPosition)); 
        byteArrayPosition+=8; 
        Debug.WriteLine(" Added signed 64-bit integer."); 
        break; 
       case 'Q': 
        outputList.Add((object)(ulong)BitConverter.ToUInt64(bytes,byteArrayPosition)); 
        byteArrayPosition+=8; 
        Debug.WriteLine(" Added unsigned 64-bit integer."); 
        break; 
       case 'l': 
        outputList.Add((object)(int)BitConverter.ToInt32(bytes, byteArrayPosition)); 
        byteArrayPosition+=4; 
        Debug.WriteLine(" Added signed 32-bit integer."); 
        break; 
       case 'L': 
        outputList.Add((object)(uint)BitConverter.ToUInt32(bytes, byteArrayPosition)); 
        byteArrayPosition+=4; 
        Debug.WriteLine(" Added unsignedsigned 32-bit integer."); 
        break; 
       case 'h': 
        outputList.Add((object)(short)BitConverter.ToInt16(bytes, byteArrayPosition)); 
        byteArrayPosition += 2; 
        Debug.WriteLine(" Added signed 16-bit integer."); 
        break; 
       case 'H': 
        outputList.Add((object)(ushort)BitConverter.ToUInt16(bytes, byteArrayPosition)); 
        byteArrayPosition += 2; 
        Debug.WriteLine(" Added unsigned 16-bit integer."); 
        break; 
       case 'b': 
        buf = new byte[1]; 
        Array.Copy(bytes,byteArrayPosition,buf,0,1); 
        outputList.Add((object)(sbyte)buf[0]); 
        byteArrayPosition++; 
        Debug.WriteLine(" Added signed byte"); 
        break; 
       case 'B': 
        buf = new byte[1]; 
        Array.Copy(bytes, byteArrayPosition, buf, 0, 1); 
        outputList.Add((object)(byte)buf[0]); 
        byteArrayPosition++; 
        Debug.WriteLine(" Added unsigned byte"); 
        break; 
       case 'x': 
        byteArrayPosition++; 
        Debug.WriteLine(" Ignoring a byte"); 
        break; 
       default: 
        throw new ArgumentException("You should not be here."); 
      } 
     } 
     return outputList.ToArray(); 
    } 

    /// <summary> 
    /// Convert an array of objects to a byte array, along with a string that can be used with Unpack. 
    /// </summary> 
    /// <param name="items">An object array of items to convert</param> 
    /// <param name="LittleEndian">Set to False if you want to use big endian output.</param> 
    /// <param name="NeededFormatStringToRecover">Variable to place an 'Unpack'-compatible format string into.</param> 
    /// <returns>A Byte array containing the objects provided in binary format.</returns> 
    public static byte[] Pack(object[] items, bool LittleEndian, out string NeededFormatStringToRecover) 
    { 

     // make a byte list to hold the bytes of output 
     List<byte> outputBytes = new List<byte>(); 

     // should we be flipping bits for proper endinanness? 
     bool endianFlip = (LittleEndian != BitConverter.IsLittleEndian); 

     // start working on the output string 
     string outString = (LittleEndian == false ? ">" : "<"); 

     // convert each item in the objects to the representative bytes 
     foreach (object o in items) 
     { 
      byte[] theseBytes = TypeAgnosticGetBytes(o); 
      if (endianFlip == true) theseBytes = (byte[])theseBytes.Reverse(); 
      outString += GetFormatSpecifierFor(o); 
      outputBytes.AddRange(theseBytes); 
     } 

     NeededFormatStringToRecover = outString; 

     return outputBytes.ToArray(); 

    } 

    public static byte[] Pack(object[] items) 
    { 
     string dummy = ""; 
     return Pack(items, true, out dummy); 
    } 
} 
+0

विस्मयकारी अनुवाद के सी # लॉजिकल समकक्ष है। पहले लूप में मामलों I और I पर विचार करने के लिए बस एक बिंदु दूसरे लूप में एल और एल के मामलों का हिस्सा होना चाहिए – Bryida

1

.NET (और इस प्रकार, सी #) में Marshal.StructureToPtr और Marshal.PtrToStructure विधियां हैं।

आप कच्चे मेमोरी को struct पर कास्ट करने के लिए इनका दुरुपयोग कर सकते हैं जैसे कि आप सी में होंगे, न कि मैं इसे इस तरह से करने की अनुशंसा करता हूं (क्योंकि यह बिल्कुल पोर्टेबल नहीं है)। तुम भी उस पर कार्रवाई करने के लिए आदेश में देशी ढेर में अपने Byte[] सरणी बफर प्राप्त करने की आवश्यकता:

T FromBuffer<T>(Byte[] buffer) where T : struct { 

    T temp = new T(); 
    int size = Marshal.SizeOf(temp); 
    IntPtr ptr = Marshal.AllocHGlobal(size); 

    Marshal.Copy(buffer, 0, ptr, size); 

    T ret = (T)Marshal.PtrToStructure(ptr, temp.GetType()); 
    Marshal.FreeHGlobal(ptr); 

    return ret; 
} 
1

BinaryWriter और BinaryReader एक बाइट सरणी के लिए मनमाने ढंग से आइटम भेज सकते हैं या एक बाइट सरणी से मनमाना आइटम पढ़ा जाएगा

var str = new MemoryStream(); 
var bw = new BinaryWriter(str); 
bw.Write(42); 
bw.Write("hello"); 
... 
var bytes = str.ToArray(); 
+0

'बाइनरी रीडर | लेखक 'लेखन/पढ़ने वाले structs, केवल primitives, तारों और बाइट सरणी का समर्थन नहीं करता है। – Dai

+0

वास्तव में - लेकिन वह वही करता है जो वह चाहता है और पैक/अनपैक – pm100

संबंधित मुद्दे