Come si ottiene una stringa da un MemoryStream?


532

Se mi viene dato un oggetto MemoryStreamche so che è stato popolato con un String, come posso ottenere un Stringritorno?


1
Mai del tutto sicuro se reader.close è sempre richiesto. Ho avuto problemi in passato, quindi di norma faccio sempre solo per essere al sicuro.
Crusty,

Risposte:


468

Questo esempio mostra come leggere e scrivere una stringa in MemoryStream.


Imports System.IO

Module Module1
  Sub Main()
    ' We don't need to dispose any of the MemoryStream 
    ' because it is a managed object. However, just for 
    ' good practice, we'll close the MemoryStream.
    Using ms As New MemoryStream
      Dim sw As New StreamWriter(ms)
      sw.WriteLine("Hello World")
      ' The string is currently stored in the 
      ' StreamWriters buffer. Flushing the stream will 
      ' force the string into the MemoryStream.
      sw.Flush()
      ' If we dispose the StreamWriter now, it will close 
      ' the BaseStream (which is our MemoryStream) which 
      ' will prevent us from reading from our MemoryStream
      'sw.Dispose()

      ' The StreamReader will read from the current 
      ' position of the MemoryStream which is currently 
      ' set at the end of the string we just wrote to it. 
      ' We need to set the position to 0 in order to read 
      ' from the beginning.
      ms.Position = 0
      Dim sr As New StreamReader(ms)
      Dim myStr = sr.ReadToEnd()
      Console.WriteLine(myStr)

      ' We can dispose our StreamWriter and StreamReader 
      ' now, though this isn't necessary (they don't hold 
      ' any resources open on their own).
      sw.Dispose()
      sr.Dispose()
    End Using

    Console.WriteLine("Press any key to continue.")
    Console.ReadKey()
  End Sub
End Module

3
Non eliminerà StreamWriter quando la funzione non rientra nell'ambito di applicazione?
Tim Keating

14
Dispose non viene chiamato quando una variabile esce dall'ambito. Finalize verrà chiamato quando il GC vi si avvicina, ma Dispose è qualcosa che deve essere chiamato prima che la variabile esca dall'ambito. Non lo chiamo sopra perché so che l'implementazione di StreamWriter e StreamReader non richiedono la chiamata a Dispose, passa semplicemente la chiamata allo stream sottostante. Tuttavia, un argomento legittimo può essere fatto per chiamare Dipose per tutto ciò che implementa IDisposable poiché non è possibile garantire che una versione futura non richieda che venga eliminata.
Brian,

12
@MichaelEakins Perché la risposta dovrebbe essere anche in C #, quando la domanda è taggata come VB.Net?
Rowland Shaw,

1
Sono contento di aver appreso degli "aiutanti" che passano la chiamata dispose ai loro flussi sottostanti, ma questa sembra una cattiva decisione di progettazione.
Gerard ONeill,

2
Questa decisione è stata attenuata in seguito: msdn.microsoft.com/en-us/library/…
Mark Sowul,

310

Puoi anche usare

Encoding.ASCII.GetString(ms.ToArray());

Non penso che questo sia meno efficiente, ma non potrei giurarlo. Ti permette anche di scegliere una codifica diversa, mentre usando uno StreamReader dovresti specificarlo come parametro.


6
La codifica è nello spazio dei nomi System.Text
northben

2
Stavo cercando l'equivalente di PowerShell di questo e ho dovuto usare questo. ([System.Text.Encoding] :: ASCII) .GetString (ms.ToArray ())
Lewis

Questa soluzione è utile in quanto può essere utilizzata dopo la chiusura di MemoryStream.
Jacob Horbulyk,

2
FWIW, ho scoperto che non funzionava con stringhe molto grandi, stavo diventando OutOfMemoryExceptions. Utilizzando StreamReaderinvece risolto il problema.
Concedi H.

Dal momento che questo può essere un errore: non è a conoscenza del segno dell'ordine dei byte e potrebbe includere un esadecimale 00all'inizio della stringa. 00 3C 3F-> .<?in Hex Editor, ma in VS o Notepad ++: <?. Quindi non puoi vedere la differenza anche se confronti le stringhe a occhio, solo uno strumento di confronto o un editor esadecimale mostreranno la differenza. Se lo usi ancora, pensa a String.TrimStart. Vedi: docs.microsoft.com/en-us/dotnet/api/…
Skalli

99

Utilizzo di StreamReader per convertire MemoryStream in una stringa.

<Extension()> _
Public Function ReadAll(ByVal memStream As MemoryStream) As String
    ' Reset the stream otherwise you will just get an empty string.
    ' Remember the position so we can restore it later.
    Dim pos = memStream.Position
    memStream.Position = 0

    Dim reader As New StreamReader(memStream)
    Dim str = reader.ReadToEnd()

    ' Reset the position so that subsequent writes are correct.
    memStream.Position = pos

    Return str
End Function

3
Impostando Posizione su 0 si limita la possibilità di riutilizzo del metodo: è meglio lasciare che sia il chiamante a gestirlo. Che cosa succede se lo stream contiene dati prima della stringa che il chiamante sa come gestire?
Alex Lyman,

1
L'istruzione using assicurerà che lo StreamReader venga eliminato, ma la documentazione indica che StreamReader chiude lo stream sottostante quando viene eliminato. Pertanto, il tuo metodo chiude MemoryStream che viene passato, il che è concettualmente poco piacevole per i chiamanti anche se dubito che MemoryStream.Dispose faccia molto.
Trillian,

Hai ragione. In genere è una cattiva idea utilizzare il metodo Dispose sulle classi helper del flusso, soprattutto se il flusso viene passato a un metodo come parametro. Aggiornerò questa risposta. Ho anche una risposta più completa di seguito.
Brian

Se decompili quelle classi, vedrai che il metodo dispose chiama semplicemente Dispose () su tutti i flussi che non sono nulli nell'istanza (TextWriter, MemoryStream, ecc.)
Sinaesthetic

39

utilizzare uno StreamReader , quindi è possibile utilizzare il metodo ReadToEnd che restituisce una stringa.


12
Voglio solo menzionare che il Basestreamdovrebbe ha impostato la sua posizione su 0. Mi piace memoryStream.Position = 0;.
Aykut Çevik

26
byte[] array = Encoding.ASCII.GetBytes("MyTest1 - MyTest2");
MemoryStream streamItem = new MemoryStream(array);

// convert to string
StreamReader reader = new StreamReader(streamItem);
string text = reader.ReadToEnd();

22

Le soluzioni precedenti non funzionavano nei casi in cui è coinvolta la codifica. Ecco - una specie di "vita reale" - esempio come farlo correttamente ...

using(var stream = new System.IO.MemoryStream())
{
  var serializer = new DataContractJsonSerializer(typeof(IEnumerable<ExportData>),  new[]{typeof(ExportData)}, Int32.MaxValue, true, null, false);               
  serializer.WriteObject(stream, model);  


  var jsonString = Encoding.Default.GetString((stream.ToArray()));
}

15

In questo caso, se si desidera veramente utilizzare il ReadToEndmetodo in MemoryStreammodo semplice, è possibile utilizzare questo metodo di estensione per raggiungere questo obiettivo:

public static class SetExtensions
{
    public static string ReadToEnd(this MemoryStream BASE)
    {
        BASE.Position = 0;
        StreamReader R = new StreamReader(BASE);
        return R.ReadToEnd();
    }
}

E puoi usare questo metodo in questo modo:

using (MemoryStream m = new MemoryStream())
{
    //for example i want to serialize an object into MemoryStream
    //I want to use XmlSeralizer
    XmlSerializer xs = new XmlSerializer(_yourVariable.GetType());
    xs.Serialize(m, _yourVariable);

    //the easy way to use ReadToEnd method in MemoryStream
    MessageBox.Show(m.ReadToEnd());
}

11

Questo esempio mostra come leggere una stringa da un MemoryStream, in cui ho usato una serializzazione (utilizzando DataContractJsonSerializer), passare la stringa da un server al client, quindi come recuperare MemoryStream dalla stringa passata come parametro, quindi , deserializza MemoryStream.

Ho usato parti di post diversi per eseguire questo esempio.

Spero che questo aiuti.

using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.Threading;

namespace JsonSample
{
    class Program
    {
        static void Main(string[] args)
        {
            var phones = new List<Phone>
            {
                new Phone { Type = PhoneTypes.Home, Number = "28736127" },
                new Phone { Type = PhoneTypes.Movil, Number = "842736487" }
            };
            var p = new Person { Id = 1, Name = "Person 1", BirthDate = DateTime.Now, Phones = phones };

            Console.WriteLine("New object 'Person' in the server side:");
            Console.WriteLine(string.Format("Id: {0}, Name: {1}, Birthday: {2}.", p.Id, p.Name, p.BirthDate.ToShortDateString()));
            Console.WriteLine(string.Format("Phone: {0} {1}", p.Phones[0].Type.ToString(), p.Phones[0].Number));
            Console.WriteLine(string.Format("Phone: {0} {1}", p.Phones[1].Type.ToString(), p.Phones[1].Number));

            Console.Write(Environment.NewLine);
            Thread.Sleep(2000);

            var stream1 = new MemoryStream();
            var ser = new DataContractJsonSerializer(typeof(Person));

            ser.WriteObject(stream1, p);

            stream1.Position = 0;
            StreamReader sr = new StreamReader(stream1);
            Console.Write("JSON form of Person object: ");
            Console.WriteLine(sr.ReadToEnd());

            Console.Write(Environment.NewLine);
            Thread.Sleep(2000);

            var f = GetStringFromMemoryStream(stream1);

            Console.Write(Environment.NewLine);
            Thread.Sleep(2000);

            Console.WriteLine("Passing string parameter from server to client...");

            Console.Write(Environment.NewLine);
            Thread.Sleep(2000);

            var g = GetMemoryStreamFromString(f);
            g.Position = 0;
            var ser2 = new DataContractJsonSerializer(typeof(Person));
            var p2 = (Person)ser2.ReadObject(g);

            Console.Write(Environment.NewLine);
            Thread.Sleep(2000);

            Console.WriteLine("New object 'Person' arrived to the client:");
            Console.WriteLine(string.Format("Id: {0}, Name: {1}, Birthday: {2}.", p2.Id, p2.Name, p2.BirthDate.ToShortDateString()));
            Console.WriteLine(string.Format("Phone: {0} {1}", p2.Phones[0].Type.ToString(), p2.Phones[0].Number));
            Console.WriteLine(string.Format("Phone: {0} {1}", p2.Phones[1].Type.ToString(), p2.Phones[1].Number));

            Console.Read();
        }

        private static MemoryStream GetMemoryStreamFromString(string s)
        {
            var stream = new MemoryStream();
            var sw = new StreamWriter(stream);
            sw.Write(s);
            sw.Flush();
            stream.Position = 0;
            return stream;
        }

        private static string GetStringFromMemoryStream(MemoryStream ms)
        {
            ms.Position = 0;
            using (StreamReader sr = new StreamReader(ms))
            {
                return sr.ReadToEnd();
            }
        }
    }

    [DataContract]
    internal class Person
    {
        [DataMember]
        public int Id { get; set; }
        [DataMember]
        public string Name { get; set; }
        [DataMember]
        public DateTime BirthDate { get; set; }
        [DataMember]
        public List<Phone> Phones { get; set; }
    }

    [DataContract]
    internal class Phone
    {
        [DataMember]
        public PhoneTypes Type { get; set; }
        [DataMember]
        public string Number { get; set; }
    }

    internal enum PhoneTypes
    {
        Home = 1,
        Movil = 2
    }
}

5

Una versione leggermente modificata della risposta di Brian consente la gestione facoltativa dell'inizio della lettura. Questo sembra essere il metodo più semplice. probabilmente non è il più efficiente, ma facile da capire e da usare.

Public Function ReadAll(ByVal memStream As MemoryStream, Optional ByVal startPos As Integer = 0) As String
    ' reset the stream or we'll get an empty string returned
    ' remember the position so we can restore it later
    Dim Pos = memStream.Position
    memStream.Position = startPos

    Dim reader As New StreamReader(memStream)
    Dim str = reader.ReadToEnd()

    ' reset the position so that subsequent writes are correct
    memStream.Position = Pos

    Return str
End Function

3
non aggiunge davvero nulla di nuovo alla risposta di Brian
Luis Filipe,

5

Perché non creare un metodo di estensione piacevole sul tipo MemoryStream?

public static class MemoryStreamExtensions
{

    static object streamLock = new object();

    public static void WriteLine(this MemoryStream stream, string text, bool flush)
    {
        byte[] bytes = Encoding.UTF8.GetBytes(text + Environment.NewLine);
        lock (streamLock)
        {
            stream.Write(bytes, 0, bytes.Length);
            if (flush)
            {
                stream.Flush();
            }
        }
    }

    public static void WriteLine(this MemoryStream stream, string formatString, bool flush, params string[] strings)
    {
        byte[] bytes = Encoding.UTF8.GetBytes(String.Format(formatString, strings) + Environment.NewLine);
        lock (streamLock)
        {
            stream.Write(bytes, 0, bytes.Length);
            if (flush)
            {
                stream.Flush();
            }
        }
    }

    public static void WriteToConsole(this MemoryStream stream)
    {
        lock (streamLock)
        {
            long temporary = stream.Position;
            stream.Position = 0;
            using (StreamReader reader = new StreamReader(stream, Encoding.UTF8, false, 0x1000, true))
            {
                string text = reader.ReadToEnd();
                if (!String.IsNullOrEmpty(text))
                {
                    Console.WriteLine(text);
                }
            }
            stream.Position = temporary;
        }
    }
}

Ovviamente, fai attenzione quando usi questi metodi insieme a quelli standard. :) ... dovrai usare il pratico streamLock se lo fai, per la concorrenza.


0

Ho bisogno di integrarmi con una classe che ha bisogno di uno Stream per scrivere su di esso:

XmlSchema schema;
// ... Use "schema" ...

var ret = "";

using (var ms = new MemoryStream())
{
    schema.Write(ms);
    ret = Encoding.ASCII.GetString(ms.ToArray());
}
//here you can use "ret"
// 6 Lines of code

Creo una classe semplice che può aiutare a ridurre le righe di codice per l'uso multiplo:

public static class MemoryStreamStringWrapper
{
    public static string Write(Action<MemoryStream> action)
    {
        var ret = "";
        using (var ms = new MemoryStream())
        {
            action(ms);
            ret = Encoding.ASCII.GetString(ms.ToArray());
        }

        return ret;
    }
}

quindi è possibile sostituire l'esempio con una singola riga di codice

var ret = MemoryStreamStringWrapper.Write(schema.Write);
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.