Wie erhält man einen String aus einem MemoryStream?

532

Wenn ich ein bestimmtes bin , MemoryStreamdass ich weiß , hat mit einem besiedelt String, wie bekomme ich einen Stringwieder aus?

Brian
quelle
1
Nie ganz sicher, ob reader.close immer erforderlich ist. Ich hatte in der Vergangenheit Probleme, deshalb tue ich es in der Regel immer nur, um auf der sicheren Seite zu sein.
Crusty

Antworten:

468

Dieses Beispiel zeigt, wie Sie einen String in einen MemoryStream lesen und schreiben.


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
Brian
quelle
3
Wird der StreamWriter nicht entsorgt, wenn die Funktion ohnehin nicht mehr verfügbar ist?
Tim Keating
14
Dispose wird nicht aufgerufen, wenn eine Variable den Gültigkeitsbereich verlässt. Finalize wird aufgerufen, wenn der GC dazu kommt, aber Dispose muss aufgerufen werden, bevor die Variable den Gültigkeitsbereich verlässt. Ich rufe es oben nicht auf, weil ich weiß, dass für die Implementierung von StreamWriter und StreamReader Dispose nicht aufgerufen werden muss, sondern der Aufruf nur an den zugrunde liegenden Stream weitergeleitet wird. Es kann jedoch ein legitimes Argument dafür angeführt werden, Dipose für alles aufzurufen, was IDisposable implementiert, da Sie nicht garantieren können, dass eine zukünftige Version nicht entsorgt werden muss.
Brian
12
@MichaelEakins Warum sollte die Antwort überhaupt in C # sein, wenn die Frage als VB.Net markiert ist?
Rowland Shaw
1
Ich bin froh, dass ich erfahren habe, dass die "Helfer" den Entsorgungsaufruf an ihre zugrunde liegenden Streams weiterleiten, aber dies scheint eine schlechte Designentscheidung zu sein.
Gerard ONeill
2
Diese Entscheidung wurde später gemildert: msdn.microsoft.com/en-us/library/…
Mark Sowul
310

Sie können auch verwenden

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

Ich denke nicht, dass dies weniger effizient ist, aber ich konnte es nicht schwören. Außerdem können Sie eine andere Codierung auswählen, während Sie bei Verwendung eines StreamReader diese als Parameter angeben müssen.

Codierer
quelle
6
Die Codierung erfolgt im System.Text-Namespace
Northben
2
Ich suchte nach dem PowerShell-Äquivalent dazu und musste dieses verwenden. ([System.Text.Encoding] :: ASCII) .GetString (ms.ToArray ())
Lewis
Diese Lösung ist nützlich, da sie verwendet werden kann, nachdem der MemoryStream geschlossen wurde.
Jacob Horbulyk
2
FWIW, ich fand, dass dies mit sehr großen Saiten nicht funktionierte, ich bekam OutOfMemoryExceptions. Die Verwendung eines StreamReaderstattdessen löste das Problem.
Grant H.
Da dies eine Gefahr sein kann: Es ist die Byte-Ordnungsmarke nicht bekannt und es kann ein Hex 00am Anfang der Zeichenfolge enthalten sein. 00 3C 3F-> .<?im Hex-Editor, aber in VS oder Notepad ++ : <?. Sie können den Unterschied also nicht erkennen, selbst wenn Sie die Zeichenfolgen mit dem Auge vergleichen. Nur ein Vergleichstool oder ein Hex-Editor zeigen den Unterschied an. Wenn Sie es weiterhin verwenden, denken Sie an String.TrimStart. Siehe: docs.microsoft.com/en-us/dotnet/api/…
Skalli
99

Verwenden eines StreamReader zum Konvertieren des MemoryStream in einen String.

<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
Brian
quelle
3
Wenn Sie die Position auf 0 setzen, wird die Wiederverwendbarkeit der Methode eingeschränkt. Am besten lassen Sie den Anrufer dies verwalten. Was ist, wenn der Stream Daten vor der Zeichenfolge enthält, mit denen der Aufrufer umgehen kann?
Alex Lyman
1
Die using-Anweisung stellt sicher, dass Ihr StreamReader entsorgt wird. Die Dokumentation besagt jedoch, dass StreamReader den zugrunde liegenden Stream schließt, wenn er entsorgt wird. Daher schließt Ihre Methode den MemoryStream, den sie übergeben wird, was für Anrufer konzeptionell uncool ist, selbst wenn ich bezweifle, dass MemoryStream.Dispose viel bewirkt.
Trillian
Du hast Recht. Es ist normalerweise eine schlechte Idee, die Dispose-Methode für die Stream-Hilfsklassen zu verwenden, insbesondere wenn der Stream als Parameter an eine Methode übergeben wird. Ich werde diese Antwort aktualisieren. Ich habe auch eine vollständigere Antwort unten.
Brian
Wenn Sie diese Klassen dekompilieren, werden Sie feststellen, dass die dispose-Methode einfach Dispose () für alle Streams aufruft, die in der Instanz nicht null sind (TextWriter, MemoryStream usw.)
Sinaesthetic
39

Verwenden Sie einen StreamReader . Anschließend können Sie die ReadToEnd- Methode verwenden, die eine Zeichenfolge zurückgibt.

Darren Kopp
quelle
12
Ich möchte nur erwähnen, dass das Basestreamsollte seine Position auf 0 gesetzt hat 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();
Sadjad Khazaie
quelle
22

Frühere Lösungen würden in Fällen, in denen es um Codierung geht, nicht funktionieren. Hier ist - eine Art "echtes Leben" - ein Beispiel, wie man das richtig macht ...

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()));
}
Arek Bal
quelle
15

In diesem Fall , wenn Sie wirklich verwenden mögen ReadToEndMethode in MemoryStreameiner einfachen Möglichkeit, können Sie diese Erweiterungsmethode verwenden , um dies zu erreichen:

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

Und Sie können diese Methode folgendermaßen anwenden:

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());
}
Mehdi Khademloo
quelle
11

Dieses Beispiel zeigt, wie Sie eine Zeichenfolge aus einem MemoryStream lesen, in dem ich eine Serialisierung verwendet habe (mit DataContractJsonSerializer), die Zeichenfolge von einem Server an den Client übergeben und anschließend den MemoryStream aus der als Parameter übergebenen Zeichenfolge wiederherstellen , deserialisieren Sie den MemoryStream.

Ich habe Teile verschiedener Beiträge verwendet, um dieses Beispiel durchzuführen.

Hoffe das hilft.

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
    }
}
Sebastian Ferrari
quelle
5

Eine leicht modifizierte Version von Brians Antwort ermöglicht die optionale Verwaltung des Lesestarts. Dies scheint die einfachste Methode zu sein. wahrscheinlich nicht die effizienteste, aber einfach zu verstehen und zu verwenden.

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
James
quelle
3
es fügt Brian Antwort wirklich nichts Neues hinzu
Luis Filipe
5

Warum nicht eine nette Erweiterungsmethode für den MemoryStream-Typ erstellen?

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;
        }
    }
}

Seien Sie natürlich vorsichtig, wenn Sie diese Methoden in Verbindung mit den Standardmethoden verwenden. :) ... Sie müssen dieses praktische streamLock verwenden, wenn Sie dies für die Parallelität tun.

Alexandru
quelle
0

Ich muss mich in eine Klasse integrieren, die einen Stream zum Schreiben benötigt:

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

Ich erstelle eine einfache Klasse, die dazu beitragen kann, Codezeilen für die mehrfache Verwendung zu reduzieren:

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;
    }
}

Dann können Sie das Beispiel durch eine einzelne Codezeile ersetzen

var ret = MemoryStreamStringWrapper.Write(schema.Write);
Riccardo Bassilichi
quelle