एक स्ट्रीम से एक बाइट सरणी बनाना


913

इनपुट स्ट्रीम से बाइट सरणी बनाने के लिए पसंदीदा तरीका क्या है?

यहां .NET 3.5 के साथ मेरा वर्तमान समाधान है।

Stream s;
byte[] b;

using (BinaryReader br = new BinaryReader(s))
{
    b = br.ReadBytes((int)s.Length);
}

क्या अब भी धारा का हिस्सा पढ़ना और लिखना बेहतर है?


60
बेशक, एक और सवाल यह होना चाहिए कि एक स्ट्रीम से एक बाइट [] बनाई जाए ... बड़े डेटा के लिए, स्ट्रीम के साथ-साथ, एक स्ट्रीम के साथ व्यवहार करना बेहतर होता है!
मार्क Gravell

2
वास्तव में आपको बाइट के बजाय एक धारा का उपयोग करना चाहिए []। लेकिन कुछ सिस्टम API हैं जो स्ट्रीम का समर्थन नहीं करते हैं। उदाहरण के लिए, आप एक धारा से X509Certificate2 नहीं बना सकते , आपको इसे एक बाइट [] (या एक स्ट्रिंग) देनी होगी। इस मामले में यह ठीक है क्योंकि x509 प्रमाणपत्र संभवतः बड़ा डेटा नहीं है ।
0

जवाबों:


1293

यह वास्तव में इस बात पर निर्भर करता है कि आप भरोसा कर सकते हैं या नहीं s.Length। कई धाराओं के लिए, आपको पता नहीं है कि कितना डेटा होगा। ऐसे मामलों में - और .NET 4 से पहले - मैं इस तरह कोड का उपयोग करूंगा:

public static byte[] ReadFully(Stream input)
{
    byte[] buffer = new byte[16*1024];
    using (MemoryStream ms = new MemoryStream())
    {
        int read;
        while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
        {
            ms.Write(buffer, 0, read);
        }
        return ms.ToArray();
    }
}

.NET 4 और इसके बाद के संस्करण में, मैं उपयोग करूंगा Stream.CopyTo, जो मूल रूप से मेरे कोड में लूप के बराबर है - MemoryStreamकॉल करें, stream.CopyTo(ms)और फिर वापस लौटें ms.ToArray()। काम हो गया।

मुझे शायद समझाना चाहिए कि मेरा जवाब दूसरों की तुलना में लंबा क्यों है। Stream.Readयह गारंटी नहीं देता कि यह वह सब कुछ पढ़ेगा जो उसने माँगा है। यदि आप एक नेटवर्क स्ट्रीम से पढ़ रहे हैं, उदाहरण के लिए, यह एक पैकेट के मूल्य को पढ़ सकता है और फिर वापस आ सकता है, भले ही अधिक डेटा जल्द ही होगा। BinaryReader.Readस्ट्रीम के अंत तक या आपके निर्दिष्ट आकार तक चलते रहेंगे, लेकिन आपको अभी भी शुरू करने के लिए आकार जानना होगा।

उपरोक्त विधि MemoryStreamडेटा को पढ़ने तक (और नकल में ) जारी रखेगी । यह तब MemoryStreamकिसी सरणी में डेटा की एक प्रति वापस करने के लिए कहता है । यदि आप आकार को शुरू करने के लिए जानते हैं - या लगता है कि आप आकार को जानते हैं, तो सुनिश्चित किए बिना - आप MemoryStreamउस आकार के साथ शुरू करने के लिए निर्माण कर सकते हैं । इसी तरह आप अंत में एक चेक डाल सकते हैं, और यदि स्ट्रीम की लंबाई बफर के समान आकार है ( MemoryStream.GetBufferतब तक वापस लौटा दी जाती है ) तो आप बफर को वापस कर सकते हैं। इसलिए उपरोक्त कोड काफी अनुकूलित नहीं है, लेकिन कम से कम सही होगा। यह धारा को बंद करने के लिए कोई जिम्मेदारी नहीं लेता है - कॉल करने वाले को ऐसा करना चाहिए।

अधिक जानकारी के लिए यह लेख देखें (और एक वैकल्पिक कार्यान्वयन)।


9
@, यह उल्लेख योग्य हो सकता है yoda.arachsys.com/csharp/readbinary.html
सैम केसर

6
@ जेफ़: हमारे पास वास्तव में यहाँ संदर्भ नहीं है, लेकिन यदि आप एक स्ट्रीम में लिख रहे हैं, तो हाँ आपको पढ़ने से पहले इसे "रिवाइंड" करना होगा। वहाँ सिर्फ एक "कर्सर" कह रहा है जहाँ आप स्ट्रीम के भीतर हैं - पढ़ने के लिए एक नहीं और लिखने के लिए एक अलग।
जॉन स्कीट

5
@ जेफ: यह कॉल करने वाले की जिम्मेदारी है। सब के बाद, धारा प्राप्य नहीं हो सकती (उदाहरण के लिए एक नेटवर्क स्ट्रीम) या बस इसे रिवाइंड करने की आवश्यकता नहीं हो सकती है।
जॉन स्कीट

18
क्या मैं पूछ सकता हूं कि 16*1024विशेष रूप से क्यों ?
आयनम डोनॉटकेयर

5
@ अन्याय_नाम: मुझे नहीं पता कि इसका कोई महत्व है, लेकिन (16 * 1024) Int16.MaxValue का आधा होता है :)
caesay

734

जबकि जॉन का जवाब सही है, वह कोड फिर से लिख रहा है जो पहले से मौजूद है CopyTo। इसलिए .Net 4 के लिए संदीप के समाधान का उपयोग करें, लेकिन .Net के पिछले संस्करण के लिए जॉन के उत्तर का उपयोग करें। संदीप का कोड CopyToकई स्थितियों में, अपवाद के रूप में "उपयोग" के उपयोग से सुधारा जाएगा , काफी संभावना है और MemoryStreamनिपटा नहीं जाएगा ।

public static byte[] ReadFully(Stream input)
{
    using (MemoryStream ms = new MemoryStream())
    {
        input.CopyTo(ms);
        return ms.ToArray();
    }
}

6
आपके उत्तर और जॉन के बीच क्या अंतर है? इसके अलावा मुझे यह इनपुट करना चाहिए। कॉपी = काम करने के लिए CopyTo के लिए 0 =।
जेफ

1
@nathan, वेब क्लाइंट से फाइल पढता है (filizeize = 1mb) - iis को पूरे 1mb को अपनी मेमोरी में लोड करना होगा?
रॉय नमिर

5
@Jeff, मेरा जवाब केवल .Net 4 या इसके बाद के संस्करण पर काम करेगा, जोंस बाद के संस्करण में हमें प्रदान की गई कार्यक्षमता को फिर से लिखना करके कम संस्करणों पर काम करेगा। आप सही हैं कि CopyTo केवल वर्तमान स्थिति से कॉपी करेगा, यदि आपके पास एक सीचेबल स्ट्रीम है और आप शुरुआत से कॉपी करना चाहते हैं तो आप अपने कोड या इनपुट का उपयोग करके शुरुआत में जा सकते हैं। Seek (0, SeekOrigin.Begin), हालांकि कई मामलों में आपकी स्ट्रीम सीक्वेबल नहीं हो सकती है।
नाथन फिलिप्स

5
यह जाँच के लायक हो सकता है अगर inputपहले से ही एक MemorySteamछोटी और छोटी सर्कुलेटिंग है। मुझे पता है कि फोन करने वाले के लिए यह बेवकूफी होगी MemoryStreamलेकिन ...
जॉड्रेल

3
@ जॉडरेल, बिल्कुल ऐसा। आप स्मृति में छोटी नदियों के लाखों लोगों कॉपी कर रहे हैं और उनमें से एक एक है MemoryStreamतो अनुकूलन अपने संदर्भ में समझ में आता है कि क्या समय समय लिया के खिलाफ प्रकार रूपांतरण के लाखों करने के लिए ले लिया की तुलना एक एक बात यह है कि नकल करने के लिए है MemoryStreamमें एक और MemoryStream
नाथन फिलिप्स

114

केवल यह बताना चाहते हैं कि यदि आपके पास मेमोरीस्ट्रीम है तो आप उसके memorystream.ToArray()लिए पहले से ही हैं ।

इसके अलावा, यदि आप अज्ञात या अलग-अलग उपप्रकारों की धाराओं के साथ काम कर रहे हैं और आप एक प्राप्त कर सकते हैं MemoryStream, तो आप उन मामलों के लिए उक्त विधि पर रिले कर सकते हैं और फिर भी दूसरों के लिए स्वीकृत उत्तर का उपयोग कर सकते हैं, जैसे:

public static byte[] StreamToByteArray(Stream stream)
{
    if (stream is MemoryStream)
    {
        return ((MemoryStream)stream).ToArray();                
    }
    else
    {
        // Jon Skeet's accepted answer 
        return ReadFully(stream);
    }
}

1
हुह, क्या सभी upvotes के लिए कर रहे हैं? यहां तक ​​कि सबसे उदार मान्यताओं के साथ, यह केवल उन धाराओं के लिए काम करता है जो पहले से ही MemoryStreamएस हैं। बेशक उदाहरण भी स्पष्ट रूप से अधूरा है, यह कैसे एक असमान परिवर्तनशील चर का उपयोग कर रहा है।
रोमन स्टार्कोव जूल

3
यह सही है, इस ओर इशारा करने के लिए धन्यवाद। बिंदु अभी भी मेमोरीस्ट्रीम के लिए खड़ा है, इसलिए मैंने इसे प्रतिबिंबित करने के लिए तय किया।
फर्नांडो नीरा

बस उल्लेख है कि MemoryStream के लिए एक और संभावना है MemoryStream.GetBuffer (), हालांकि इसमें कुछ गोचर्स शामिल हैं। देखें stackoverflow.com/questions/1646193/... और krishnabhargav.blogspot.dk/2009/06/...
RenniePet

4
यह वास्तव में स्कीट कोड में एक बग का परिचय देता है; यदि आप कॉल करते हैं stream.Seek(1L, SeekOrigin.Begin), इससे पहले कि आप आसानी से आह्वान करें, यदि स्ट्रीम एक मेमोरी स्ट्रीम है तो आपको किसी अन्य स्ट्रीम की तुलना में 1 अधिक बाइट मिलेगी। यदि कॉलर को यह पढ़ने की उम्मीद है कि वर्तमान स्थिति स्ट्रीम के अंत तक कहां है, तो आपको उपयोग नहीं करना चाहिए CopyToया ToArray(); ज्यादातर मामलों में यह एक मुद्दा नहीं होगा, लेकिन अगर कॉल करने वाले को इस विचित्र व्यवहार के बारे में नहीं पता है तो वे भ्रमित होंगे।
leat

67
MemoryStream ms = new MemoryStream();
file.PostedFile.InputStream.CopyTo(ms);
var byts = ms.ToArray();
ms.Dispose();

9
मेमोरी विखंडन से बचने के लिए मेमोरीस्ट्रीम को "नई मेमोरीस्ट्रीम (फाइल.पोस्टेडफाइल.कॉन्टेंट लैंथ)" के साथ बनाया जाना चाहिए।
दान रैंडोल्फ

52

बस मेरा युगल सेंट ... मेरे द्वारा उपयोग की जाने वाली प्रथा को इस तरह से कस्टम हेल्पर के रूप में व्यवस्थित करना है

public static class StreamHelpers
{
    public static byte[] ReadFully(this Stream input)
    {
        using (MemoryStream ms = new MemoryStream())
        {
            input.CopyTo(ms);
            return ms.ToArray();
        }
    }
}

कॉन्फ़िगर फ़ाइल में नाम स्थान जोड़ें और इसे अपनी इच्छानुसार कहीं भी उपयोग करें


5
ध्यान दें कि यह .NET 3.5 में काम नहीं करेगा और नीचे 4.0 CopyToपर उपलब्ध नहीं था Stream
टिम

15

आप बस पूर्व के लिए मेमोरीस्ट्रीम क्लास के ToArray () पद्धति का उपयोग कर सकते हैं

MemoryStream ms = (MemoryStream)dataInStream;
byte[] imageBytes = ms.ToArray();

10

आप इसे एक्सटेंशन के साथ भी कट्टर बना सकते हैं:

namespace Foo
{
    public static class Extensions
    {
        public static byte[] ToByteArray(this Stream stream)
        {
            using (stream)
            {
                using (MemoryStream memStream = new MemoryStream())
                {
                     stream.CopyTo(memStream);
                     return memStream.ToArray();
                }
            }
        }
    }
}

और फिर इसे एक नियमित विधि के रूप में कहें:

byte[] arr = someStream.ToByteArray()

67
मुझे लगता है कि एक प्रयोग ब्लॉक में इनपुट स्ट्रीम डालना एक बुरा विचार है। उस जिम्मेदारी को कॉलिंग प्रक्रिया के साथ आराम करना चाहिए।
जेफ

7

मुझे बॉब (यानी प्रश्नकर्ता) कोड के साथ एक संकलन समय त्रुटि मिलती है। Stream.Length एक लंबा समय है जब बाइनरीरएडर.हेडबाइट्स एक पूर्णांक पैरामीटर लेता है। मेरे मामले में, मुझे लंबे समय तक सटीकता की आवश्यकता वाली धाराओं के साथ व्यवहार करने की उम्मीद नहीं है, इसलिए मैं निम्नलिखित का उपयोग करता हूं:

Stream s;
byte[] b;

if (s.Length > int.MaxValue) {
  throw new Exception("This stream is larger than the conversion algorithm can currently handle.");
}

using (var br = new BinaryReader(s)) {
  b = br.ReadBytes((int)s.Length);
}

5

यदि कोई इसे पसंद करता है, तो यहां .NET 4+ एकमात्र समाधान है, जो मेमोरीस्ट्रीम पर अनावश्यक डिसपोज कॉल के बिना एक्सटेंशन विधि के रूप में बनता है। यह एक निराशाजनक तुच्छ अनुकूलन है, लेकिन यह ध्यान देने योग्य है कि मेमोरीस्ट्रीम को डिस्पोज़ करने में विफल होना वास्तविक विफलता नहीं है।

public static class StreamHelpers
{
    public static byte[] ReadFully(this Stream input)
    {
        var ms = new MemoryStream();
        input.CopyTo(ms);
        return ms.ToArray();
    }
}

3

ऊपर वाला ठीक है ... लेकिन एसएमटीपी पर सामान भेजते समय (यदि आपको जरूरत है) तो आप डेटा भ्रष्टाचार का सामना करेंगे। मैंने कुछ और बदल दिया है जो बाइट के लिए सही ढंग से बाइट भेजने में मदद करेगा: '

using System;
using System.IO;

        private static byte[] ReadFully(string input)
        {
            FileStream sourceFile = new FileStream(input, FileMode.Open); //Open streamer
            BinaryReader binReader = new BinaryReader(sourceFile);
            byte[] output = new byte[sourceFile.Length]; //create byte array of size file
            for (long i = 0; i < sourceFile.Length; i++)
                output[i] = binReader.ReadByte(); //read until done
            sourceFile.Close(); //dispose streamer
            binReader.Close(); //dispose reader
            return output;
        }'

मैं यह नहीं देखता कि यह कोड डेटा के भ्रष्टाचार से कैसे बचता है। क्या आप इसे समझा सकते हैं?
निप्पेय

मान लीजिए कि आपके पास एक तस्वीर है और आप इसे एसएमटीपी के माध्यम से भेजना चाहते हैं। आप शायद base64 एन्कोडिंग का उपयोग करेंगे। यदि आप इसे बाइट्स के रूप में तोड़ते हैं, तो किसी कारण से, फ़ाइल दूषित हो जाती है। हालांकि, एक बाइनरी रीडर का उपयोग करके फ़ाइल को सफलतापूर्वक भेजा जा सकेगा।
NothinRandom

3
कुछ हद तक पुराना है, लेकिन मुझे लगा कि इस भालू का उल्लेख है - कार्यान्वयन @ नोथिनग्रैंडम स्ट्रिंग्स के साथ काम करता है, न कि धाराएं। हालांकि इस मामले में सिर्फ File.ReadAllBytes का उपयोग करना सबसे सरल होगा।
XwipeoutX

1
खतरनाक कोड शैली के कारण डाउनवोट (कोई स्वचालित निपटान / उपयोग नहीं)।
अरनी

अफसोस की बात है कि केवल -1 ने अनुमति दी, सवाल के साथ कुछ नहीं करना है, इनपुट नाम पैरामीटर फाइल किया है, डिस्पोज नहीं किया है, कोई रीडिंग बफर, कोई फिलमोड, और बाइनरी रीडर को बाइट के साथ पढ़ने के लिए क्यों?
एरिडेन anelamo

2

एक सहायक वर्ग बनाएं और इसे कहीं भी उपयोग करने की इच्छा रखें।

public static class StreamHelpers
{
    public static byte[] ReadFully(this Stream input)
    {
        using (MemoryStream ms = new MemoryStream())
        {
            input.CopyTo(ms);
            return ms.ToArray();
        }
    }
}

2

नामस्थान RestSharp.Extensions में विधि ReadAsBytes है। इस पद्धति के अंदर मेमोरीस्ट्रीम का उपयोग किया जाता है और इस पेज पर कुछ उदाहरणों की तरह समान कोड होता है लेकिन जब आप रेस्टशर का उपयोग कर रहे हैं तो यह सबसे आसान तरीका है।

using RestSharp.Extensions;
var byteArray = inputStream.ReadAsBytes();

1

आप इस एक्सटेंशन विधि का उपयोग कर सकते हैं।

public static class StreamExtensions
{
    public static byte[] ToByteArray(this Stream stream)
    {
        var bytes = new List<byte>();

        int b;
        while ((b = stream.ReadByte()) != -1)
            bytes.Add((byte)b);

        return bytes.ToArray();
    }
}

1

यह वह फ़ंक्शन है जिसका मैं उपयोग कर रहा हूं, परीक्षण किया है और अच्छी तरह से काम किया है। कृपया ध्यान रखें कि 'इनपुट' शून्य नहीं होना चाहिए और 'input.position' को पढ़ने से पहले '0' पर रीसेट कर देना चाहिए अन्यथा यह रीड लूप को तोड़ देगा और सरणी में बदलने के लिए कुछ भी नहीं पढ़ेगा।

    public static byte[] StreamToByteArray(Stream input)
    {
        if (input == null)
            return null;
        byte[] buffer = new byte[16 * 1024];
        input.Position = 0;
        using (MemoryStream ms = new MemoryStream())
        {
            int read;
            while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
            {
                ms.Write(buffer, 0, read);
            }
            byte[] temp = ms.ToArray();

            return temp;
        }
    }

-1
public static byte[] ToByteArray(Stream stream)
    {
        if (stream is MemoryStream)
        {
            return ((MemoryStream)stream).ToArray();
        }
        else
        {
            byte[] buffer = new byte[16 * 1024];
            using (MemoryStream ms = new MemoryStream())
            {
                int read;
                while ((read = stream.Read(buffer, 0, buffer.Length)) > 0)
                {
                    ms.Write(buffer, 0, read);
                }
                return ms.ToArray();
            }
        }            
    }

आपने बस # 1 और # 3 उत्तर से कुछ भी मूल्यवान जोड़े बिना कोड की नकल की। कृपया ऐसा मत करो। :)
कोडकेस्टर

जब आप एक कोड जोड़ते हैं, तो शीघ्र ही अपने प्रस्तावित समाधान का भी वर्णन करें।
याकूबोम

-5

मैं इसे एक लाइन पर काम करने में सक्षम था:

byte [] byteArr= ((MemoryStream)localStream).ToArray();

जैसा कि johnnyRose द्वारा स्पष्ट किया गया है , उपरोक्त कोड केवल मेमोरीस्ट्रीम के लिए काम करेगा


2
क्या होगा अगर localStreamएक नहीं है MemoryStream? यह कोड विफल हो जाएगा।
जॉन जॉनी

LocalStream को एक स्ट्रीम आधारित ऑब्जेक्ट होना चाहिए। स्ट्रीम आधारित ऑब्जेक्ट के बारे में अधिक जानकारी यहां stackoverflow.com/questions/8156896/…
Abba

1
क्या मैं सुझाव देने के लिए कोशिश कर रहा था, अगर आप डाली करने की कोशिश localStreamएक करने के लिए MemoryStreamहै, लेकिन localStreamहै नहीं एक MemoryStreamहै, यह होगा असफल। यह कोड ठीक संकलन करेगा, लेकिन यह वास्तविक प्रकार के आधार पर, रनटाइम में विफल हो सकता है localStream। आप हमेशा मनमाने ढंग से आधार प्रकार को बच्चे के प्रकार में नहीं डाल सकते हैं; अधिक यहाँ पढ़यह एक और अच्छा उदाहरण है जो बताता है कि आप हमेशा ऐसा क्यों नहीं कर सकते।
जॉनीरोज

मेरी उपरोक्त टिप्पणी पर विस्तार से जानने के लिए: सभी मेमोरीस्ट्रीम स्ट्रीम हैं, लेकिन सभी स्ट्रीम मेमोरीस्ट्रीम नहीं हैं।
जॉनीरोज 21

सभी स्ट्रीम आधारित ऑब्जेक्ट में आधार प्रकार के रूप में स्ट्रीम है। और स्‍ट्रीम ही हमेशा मेमोरी स्‍ट्रीम में परिवर्तनीय हो सकती है। कोई फर्क नहीं पड़ता कि आप स्ट्रीम स्ट्रीम ऑब्जेक्ट को मेमोरी स्ट्रीम में डालने का प्रयास करते हैं, यह हमेशा काम करना चाहिए। यहाँ हमारा लक्ष्य स्ट्रीम ऑब्जेक्ट को बाइट्स सरणी में बदलना है। क्या आप मुझे एक सांकेतिक मामला दे सकते हैं जहां यह विफल हो जाएगा?
अब्बा
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.