C # में किसी ऑब्जेक्ट को बाइट सरणी में कैसे बदलें


101

मेरे पास वस्तुओं का एक संग्रह है जो मुझे एक बाइनरी फ़ाइल में लिखने की आवश्यकता है।

मुझे कॉम्पैक्ट होने के लिए फ़ाइल में बाइट्स की आवश्यकता है, इसलिए मैं उपयोग नहीं कर सकता BinaryFormatterBinaryFormatterdeserialization की जरूरत के लिए जानकारी के सभी प्रकार में फेंकता है।

अगर मैं कोशिश करूँ

byte[] myBytes = (byte[]) myObject 

मुझे एक रनटाइम अपवाद मिलता है।

मुझे इसकी आवश्यकता है कि मैं तेज़ हो जाऊं, इसलिए मैं बाइट्स के चारों ओर सरणियों की नकल नहीं करूंगा। मैं सिर्फ कलाकारों byte[] myBytes = (byte[]) myObjectको काम करना चाहूंगा !

ठीक है, स्पष्ट होने के लिए, मैं आउटपुट फ़ाइल में कोई मेटाडेटा नहीं रख सकता । बस वस्तु बाइट करती है। पैक वस्तु-वस्तु। प्राप्त उत्तरों के आधार पर, ऐसा लगता है कि मैं निम्न-स्तरीय Buffer.BlockCopyकोड लिखूंगा। शायद असुरक्षित कोड का उपयोग करना।

जवाबों:


176

ऑब्जेक्ट को बाइट सरणी में बदलने के लिए:

// Convert an object to a byte array
public static byte[] ObjectToByteArray(Object obj)
{
    BinaryFormatter bf = new BinaryFormatter();
    using (var ms = new MemoryStream())
    {
        bf.Serialize(ms, obj);
        return ms.ToArray();
    }
}

आपको बस इस फ़ंक्शन को अपने कोड में कॉपी करना होगा और इसे उस ऑब्जेक्ट पर भेजना होगा जिसे आपको बाइट सरणी में बदलने की आवश्यकता है। यदि आपको बाइट सरणी को किसी ऑब्जेक्ट में बदलने की आवश्यकता है तो आप नीचे दिए गए फ़ंक्शन का उपयोग कर सकते हैं:

// Convert a byte array to an Object
public static Object ByteArrayToObject(byte[] arrBytes)
{
    using (var memStream = new MemoryStream())
    {
        var binForm = new BinaryFormatter();
        memStream.Write(arrBytes, 0, arrBytes.Length);
        memStream.Seek(0, SeekOrigin.Begin);
        var obj = binForm.Deserialize(memStream);
        return obj;
    }
}

आप इन कार्यों का उपयोग कस्टम कक्षाओं के साथ कर सकते हैं। [Serializable]सीरियलाइज़ेशन को सक्षम करने के लिए आपको अपनी कक्षा में विशेषता जोड़ने की आवश्यकता है


9
मैंने यह कोशिश की और इसने सभी प्रकार के मेटाडेटा को जोड़ दिया। ओपी ने कहा कि वह मेटाडेटा नहीं चाहते हैं।
user316117

4
उल्लेख नहीं करने के लिए, हर कोई यह मान लेता है कि आप जो क्रमबद्ध करने की कोशिश कर रहे हैं वह कुछ ऐसा है जिसे आपने लिखा है, या पहले से ही क्रमबद्ध होने के लिए पूर्व निर्धारित किया गया है।
Hexum064

3
आप बाइट ऐरे को सीधे MemoryStreamदूसरे कोड उदाहरण के कंस्ट्रक्टर में पास कर सकते हैं । यह Write(...)और के उपयोग को समाप्त करेगा Seek(...)
अज्ञात 6656

41

यदि आप चाहते हैं कि क्रमबद्ध डेटा वास्तव में कॉम्पैक्ट हो, तो आप क्रमांकन विधियों को स्वयं लिख सकते हैं। इस तरह से आपके पास कम से कम ओवरहेड होगा।

उदाहरण:

public class MyClass {

   public int Id { get; set; }
   public string Name { get; set; }

   public byte[] Serialize() {
      using (MemoryStream m = new MemoryStream()) {
         using (BinaryWriter writer = new BinaryWriter(m)) {
            writer.Write(Id);
            writer.Write(Name);
         }
         return m.ToArray();
      }
   }

   public static MyClass Desserialize(byte[] data) {
      MyClass result = new MyClass();
      using (MemoryStream m = new MemoryStream(data)) {
         using (BinaryReader reader = new BinaryReader(m)) {
            result.Id = reader.ReadInt32();
            result.Name = reader.ReadString();
         }
      }
      return result;
   }

}

मेरे पास लिखने के लिए कई ints और कई string हैं?
स्मिथ

1
@ स्मिथ: हां, आप ऐसा कर सकते हैं, बस उन्हें एक दूसरे के बाद लिखिए। BinaryWriterकिसी ऐसे स्वरूप में उन्हें लिखना होगा BinaryReader, पढ़ सकते हैं जब तक आप लिखने और उन्हें उसी क्रम में पढ़ने के रूप में।
गुफ़ा

1
क्या अंतर है BinaryWriter/Readerऔर एक का उपयोग करने के लिएBinaryFormatter
स्मिथ

3
@Smith: BinaryWriter/Readerआप स्वयं का उपयोग करते हुए क्रमांकन / deserialisation करते हैं, और आप केवल उस डेटा को लिख / पढ़ सकते हैं जो बिल्कुल आवश्यक है, जितना संभव हो उतना कॉम्पैक्ट। यह देखने के BinaryFormatterलिए प्रतिबिंब का उपयोग करता है कि क्या डेटा लिखना / पढ़ना है, और एक प्रारूप का उपयोग करता है जो सभी संभावित मामलों के लिए काम करता है। इसमें धारा में प्रारूप के बारे में मेटा जानकारी भी शामिल है, जिससे और भी अधिक उपरि जोड़ता है।
गुफ़ा

1
@Smith: आप एनम को int(या यदि आपने एनम के लिए भंडारण के रूप में किसी अन्य प्रकार को निर्दिष्ट किया है) लिख सकते हैं और इसे लिख सकते हैं। जब आप इसे पढ़ते हैं तो आप इसे एनम प्रकार पर कास्ट कर सकते हैं।
गुफ़ा

31

खैर से एक डाली myObjectको byte[]कभी नहीं काम करने के लिए जा जब तक आप एक स्पष्ट रूपांतरण या अगर मिल गया है है myObject है एक byte[]। आपको किसी प्रकार की क्रमबद्धता की आवश्यकता है । वहाँ बहुत सारे हैं, जिसमें प्रोटोकॉल बफ़र्स भी शामिल हैं जो मेरे निकट है और मुझे प्रिय है। यह अंतरिक्ष और समय दोनों के मामले में बहुत "दुबला और मतलबी" है।

आप पाएंगे कि लगभग सभी सीरियलाइज़ेशन फ्रेमवर्क में महत्वपूर्ण प्रतिबंध हैं कि आप क्या-क्या सीरीज़ कर सकते हैं, हालांकि - क्रॉस-प्लेटफॉर्म होने के कारण प्रोटोकॉल बफ़र्स कुछ से अधिक हैं।

यदि आप अधिक आवश्यकताएं दे सकते हैं, तो हम आपकी अधिक सहायता कर सकते हैं - लेकिन यह कास्टिंग के रूप में सरल नहीं है ...

संपादित करें: बस इस पर प्रतिक्रिया करने के लिए:

ऑब्जेक्ट की बाइट्स को सम्‍मिलित करने के लिए मुझे अपनी बाइनरी फ़ाइल की आवश्‍यकता है। केवल बाइट्स, कोई मेटाडेटा जो भी हो। पैक वस्तु-वस्तु। तो मैं कस्टम क्रमांकन लागू करूंगा।

कृपया ध्यान रखें कि आपकी वस्तुओं में बाइट्स अक्सर संदर्भ होते हैं ... इसलिए आपको उनके साथ क्या करना है, यह जानने की आवश्यकता होगी।

मुझे लगता है कि आपको लगता है कि डिजाइन और अपने खुद के कस्टम क्रमांकन ढांचे को लागू करना आपके द्वारा कल्पना की तुलना में कठिन है।

मैं व्यक्तिगत रूप से सिफारिश करूंगा कि यदि आपको केवल कुछ विशिष्ट प्रकारों के लिए ऐसा करने की आवश्यकता है, तो आप सामान्य क्रमबद्ध रूपरेखा के साथ आने की कोशिश नहीं करते। बस एक इंस्टेंस मेथड और सभी प्रकार के स्टैटिक मेथड को लागू करें जिनकी आपको आवश्यकता है:

public void WriteTo(Stream stream)
public static WhateverType ReadFrom(Stream stream)

एक बात ध्यान में रखें: यदि आपको विरासत में मिला है तो सब कुछ अधिक मुश्किल हो जाता है। विरासत के बिना, यदि आप जानते हैं कि आप किस प्रकार से शुरू कर रहे हैं, तो आपको किसी भी प्रकार की जानकारी शामिल करने की आवश्यकता नहीं है। बेशक, वहाँ भी संस्करण की बात है - क्या आप अपने प्रकार के विभिन्न संस्करणों के साथ पिछड़े और आगे संगतता के बारे में चिंता करने की ज़रूरत है?


क्या मेरे लिए इसे "प्रोटोबॉफ़-सीएसएचआरपी-पोर्ट" (Google-code), या "dotnet-protobufs" (Git) के रूप में संदर्भित करना अधिक सही है?
मार्क Gravell

1
ऑब्जेक्ट की बाइट्स को सम्‍मिलित करने के लिए मुझे अपनी बाइनरी फ़ाइल की आवश्‍यकता है। केवल बाइट्स, कोई मेटाडेटा जो भी हो। पैक वस्तु-वस्तु। तो मैं कस्टम क्रमांकन लागू करूंगा।
चुल्हलगन

6
शून्य मेटाडेटा का जोखिम यह है कि आप तब बहुत ही संस्करण-असहिष्णु होते हैं, क्योंकि इसमें बहुत देर होने से पहले लचीलेपन की अनुमति देने के बहुत कम तरीके हैं। प्रोटोकॉल बफ़र्स बहुत डेटा-सघन है। क्या आपको वास्तव में पेंच के अतिरिक्त मोड़ की आवश्यकता है?
मार्क Gravell

@ चेतावनी: पूर्णांक के लिए निश्चित रूप से, पीबी कच्चे बाइट्स की तुलना में सघन हो सकती है ...
जॉन स्कीट

16

मैंने क्रिस्टलनिक्स का जवाब लिया और उन्हें विस्तार विधियों में बदल दिया। मुझे आशा है कि कोई और उन्हें उपयोगी पाएगा:

public static byte[] SerializeToByteArray(this object obj)
{
    if (obj == null)
    {
        return null;
    }
    var bf = new BinaryFormatter();
    using (var ms = new MemoryStream())
    {
        bf.Serialize(ms, obj);
        return ms.ToArray();
    }
}

public static T Deserialize<T>(this byte[] byteArray) where T : class
{
    if (byteArray == null)
    {
        return null;
    }
    using (var memStream = new MemoryStream())
    {
        var binForm = new BinaryFormatter();
        memStream.Write(byteArray, 0, byteArray.Length);
        memStream.Seek(0, SeekOrigin.Begin);
        var obj = (T)binForm.Deserialize(memStream);
        return obj;
    }
}

1
यह वास्तव में उपयोगी और आसान है !! धन्यवाद।
मृ

13

आप वास्तव में क्रमांकन के बारे में बात कर रहे हैं, जो कई रूप ले सकता है। चूंकि आप छोटे और द्विआधारी चाहते हैं, इसलिए प्रोटोकॉल बफ़र एक व्यवहार्य विकल्प हो सकता है - साथ ही संस्करण सहिष्णुता और पोर्टेबिलिटी भी दे सकता है। इसके विपरीत BinaryFormatter, प्रोटोकॉल बफ़र्स वायर प्रारूप में सभी प्रकार के मेटाडेटा शामिल नहीं हैं; डेटा की पहचान करने के लिए सिर्फ बहुत ही मार्मिक मार्कर।

.NET में कुछ कार्यान्वयन हैं; विशेष रूप से

मैं विनम्रतापूर्वक तर्क देता हूं कि प्रोटोबुफ़-नेट (जो मैंने लिखा था) ठेठ सी # कक्षाओं के साथ अधिक .NET-मुहावरेदार उपयोग की अनुमति देता है ("नियमित" प्रोटोकॉल-बफ़र्स कोड-पीढ़ी की मांग करता है ); उदाहरण के लिए:

[ProtoContract]
public class Person {
   [ProtoMember(1)]
   public int Id {get;set;}
   [ProtoMember(2)]
   public string Name {get;set;}
}
....
Person person = new Person { Id = 123, Name = "abc" };
Serializer.Serialize(destStream, person);
...
Person anotherPerson = Serializer.Deserialize<Person>(sourceStream);

1
यहां तक ​​कि "ट्रिक मार्कर" भी अभी भी मेटाडेटा हैं। ओपी को जो चाहिए था उसकी मेरी समझ वस्तु में डेटा के अलावा और कुछ नहीं थी। इसलिए, उदाहरण के लिए, यदि ऑब्जेक्ट 2 32-बिट पूर्णांक के साथ एक संरचना था, तो वह परिणाम को 8 बाइट्स के बाइट सरणी होने की उम्मीद करेगा।
user316117

@ user316117 जो तब संस्करण बनाने के लिए एक वास्तविक दर्द है। प्रत्येक दृष्टिकोण के फायदे और नुकसान हैं।
मार्क Gravell


प्रोटो * विशेषताओं का उपयोग करने से बचने का एक तरीका है? मैं जिन संस्थाओं का उपयोग करना चाहता हूं, वे 3 पार्टी लाइब्रेरी में हैं।
एलेक्स 75

5

यह मेरे लिए काम किया:

byte[] bfoo = (byte[])foo;

foo एक वस्तु है कि मैं 100% निश्चित हूँ कि एक बाइट सरणी है।


2

Serialization पर एक नज़र डालें , एक संपूर्ण वस्तु को एक बाइट स्ट्रीम में "कन्वर्ट" करने की तकनीक। आप इसे नेटवर्क पर भेज सकते हैं या इसे एक फ़ाइल में लिख सकते हैं और फिर बाद में किसी ऑब्जेक्ट पर वापस रख सकते हैं।


मुझे लगता है कि chuckhlogan ने स्पष्ट रूप से मना कर दिया है कि (प्रारूप == क्रमांकन)।
हेनक होल्टरमैन

@ हेंक - यह निर्भर करता है कि कारण क्या हैं; उन्होंने अतिरिक्त जानकारी का उल्लेख किया, जिसे मैं टाइप मेटाडेटा और फ़ील्ड जानकारी लेता हूं; आप उस ओवरहेड के बिना क्रमांकन का उपयोग कर सकते हैं; अभी नहीं BinaryFormatter
मार्क Gravell

2

मुझे एक वस्तु को बाइट में बदलने का एक और तरीका मिला [], यहाँ मेरा समाधान है:

IEnumerable en = (IEnumerable) myObject;
byte[] myBytes = en.OfType<byte>().ToArray();

सादर


1

किसी ऑब्जेक्ट की मेमोरी को सीधे एक्सेस करने के लिए ("कोर डंप" करने के लिए) आपको असुरक्षित कोड में प्रवेश करना होगा।

यदि आप बाइनरीविटर की तुलना में कुछ अधिक कॉम्पैक्ट चाहते हैं या एक कच्चा मेमोरी डंप आपको देगा, तो आपको कुछ कस्टम क्रमांकन कोड लिखना होगा जो ऑब्जेक्ट से महत्वपूर्ण जानकारी को निकालता है और इसे एक इष्टतम तरीके से पैक करता है।

PS संपादित करें डेटा को संपीड़ित करने के लिए एक DeflateStream में BinaryWriter दृष्टिकोण को लपेटना बहुत आसान है, जो आमतौर पर डेटा के आकार को लगभग आधा कर देता है।


1
असुरक्षित कोड पर्याप्त नहीं है। सी # और सीएलआर अभी भी आपको असुरक्षित कोड में भी एक कच्चे पॉइंटर को प्रबंधित ऑब्जेक्ट तक नहीं ले जाने देंगे, या एक संघ में दो ऑब्जेक्ट संदर्भ डाल सकते हैं।
पावेल मिनाएव

1

मेरा मानना ​​है कि आप जो करना चाह रहे हैं वह असंभव है। आपके प्रोग्राम को बंद करने के बाद फ़ाइल से ऑब्जेक्ट को पुनर्प्राप्त करने के लिए आवश्यक

रद्दी BinaryFormatterआवश्यक है।
हालांकि, वस्तु डेटा प्राप्त करना संभव है, आपको बस इसके सटीक आकार को जानने की जरूरत है (इससे अधिक कठिन लगता है):

public static unsafe byte[] Binarize(object obj, int size)
{
    var r = new byte[size];
    var rf = __makeref(obj);
    var a = **(IntPtr**)(&rf);
    Marshal.Copy(a, r, 0, size);
    return res;
}

इसके माध्यम से पुनर्प्राप्त किया जा सकता है:

public unsafe static dynamic ToObject(byte[] bytes)
{
    var rf = __makeref(bytes);
    **(int**)(&rf) += 8;
    return GCHandle.Alloc(bytes).Target;
}

कारण यह है कि उपरोक्त तरीके क्रमबद्धता के लिए काम नहीं करते हैं क्योंकि लौटे डेटा में पहले चार बाइट्स ए RuntimeTypeHandleRuntimeTypeHandleवस्तु के लेआउट / प्रकार का वर्णन करता लेकिन इसके बारे में मूल्य हर बार कार्यक्रम में भाग गया है बदल जाता है।

संपादित करें: यह बेवकूफ है कि ऐसा मत करो -> यदि आप पहले से ही वस्तु के प्रकार को जानते हैं तो निश्चित होने के लिए आप BitConvertes.GetBytes((int)typeof(yourtype).TypeHandle.Value)deserialization के समय उन बाइट्स को स्विच कर सकते हैं ।

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