Jak uzyskać ciąg z MemoryStream?

532

Jeśli otrzymam wiadomość MemoryStream, o której wiem, że została wypełniona String, jak mogę się Stringwycofać?

Brian
źródło
1
Nigdy nie jestem pewien, czy reader.close jest zawsze wymagany. W przeszłości miałem problemy, więc z reguły zawsze robię to po bezpiecznej stronie.
Crusty

Odpowiedzi:

468

Ten przykład pokazuje, jak czytać i zapisywać ciąg do 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
Brian
źródło
3
Czy i tak nie pozbędzie się StreamWriter, gdy funkcja wyjdzie poza zakres?
Tim Keating
14
Dispose nie jest wywoływane, gdy zmienna wykracza poza zakres. Finalizacja zostanie wywołana, gdy GC się do niej zbliży, ale Dispose jest czymś, co należy wywołać, zanim zmienna wyjdzie poza zakres. Nie nazywam tego powyżej, ponieważ wiem, że implementacja StreamWriter i StreamReader nie wymagają wywołania Dispose, po prostu przekazuje wywołanie do strumienia bazowego. Można jednak uzasadnić argument wywołania Dipose dla wszystkiego, co implementuje IDisposable, ponieważ nie można zagwarantować, że przyszłe wydanie nie będzie wymagało usunięcia.
Brian
12
@MichaelEakins Dlaczego odpowiedź powinna być nawet w języku C #, gdy pytanie jest oznaczone jako VB.Net?
Rowland Shaw
1
Cieszę się, że dowiedziałem się o „pomocnikach” przekazujących wezwanie do sprzedaży do swoich podstawowych strumieni, ale wydaje się, że to zła decyzja projektowa.
Gerard ONeill 17.07.13
2
Ta decyzja została później złagodzona: msdn.microsoft.com/en-us/library/…
Mark Sowul
310

Możesz także użyć

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

Nie sądzę, że jest to mniej wydajne, ale nie mogłem tego przysiąc. Pozwala również wybrać inne kodowanie, a używając StreamReadera musisz podać to jako parametr.

Coderer
źródło
6
Kodowanie znajduje się w przestrzeni nazw System.Text
northben
2
Szukałem ekwiwalentu PowerShell i musiałem tego użyć. ([System.Text.Encoding] :: ASCII) .GetString (ms.ToArray ())
Lewis
To rozwiązanie jest przydatne, ponieważ można z niego korzystać po zamknięciu pamięci MemoryStream.
Jacob Horbulyk
2
FWIW, znalazłem, że to nie działa z bardzo dużymi ciągami, dostawałem OutOfMemoryExceptions. Zastosowanie StreamReaderzamiast tego rozwiązało problem.
Grant H.
Ponieważ może to być pułapka: nie jest świadomy znaku kolejności bajtów i może zawierać Hex 00na początku łańcucha. 00 3C 3F-> .<?w Hex Editor ale w VS lub Notepad ++: <?. Tak więc nie widzisz różnicy, nawet jeśli porównasz ciągi wzrokowo, tylko narzędzie do porównywania lub edytor szesnastkowy pokaże różnicę. Jeśli nadal go używasz, pomyśl o String.TrimStart. Zobacz: docs.microsoft.com/en-us/dotnet/api/…
Skalli
99

Użycie StreamReadera do konwersji MemoryStream na ciąg.

<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
źródło
3
Ustawienie pozycji na 0 ogranicza możliwość ponownego użycia tej metody - najlepiej pozwolić temu zarządzającemu. Co jeśli strumień zawiera dane przed ciągiem, który wywołujący wie, jak sobie z tym poradzić?
Alex Lyman
1
Instrukcja using zapewni, że Twój StreamReader zostanie zutylizowany, ale dokumentacja mówi, że StreamReader zamyka strumień bazowy, gdy zostanie zutylizowany. Dlatego twoja metoda zamyka MemoryStream, który zostaje przekazany, co jest koncepcyjnie niefajne dla dzwoniących, nawet jeśli wątpię w MemoryStream.Dispose robi wiele.
Trillian
Masz rację. Zazwyczaj złym pomysłem jest użycie metody Dispose w klasach pomocników strumienia, szczególnie jeśli strumień jest przekazywany do metody jako parametr. Zaktualizuję tę odpowiedź. Mam też pełniejszą odpowiedź poniżej.
Brian
Jeśli zdekompilujesz te klasy, zobaczysz, że metoda dispose po prostu wywołuje Dispose () na wszystkich strumieniach, które nie są puste w instancji (TextWriter, MemoryStream itp.)
Sinaesthetic 24.09.2013
39

użyj StreamReader , a następnie możesz użyć metody ReadToEnd , która zwraca ciąg znaków.

Darren Kopp
źródło
12
Chcę tylko wspomnieć, że Basestreamnależy ustawić pozycję na 0. Podobnie jak 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
źródło
22

Poprzednie rozwiązania nie działały w przypadkach, w których występuje kodowanie. Oto - rodzaj „prawdziwego życia” - przykład, jak zrobić to poprawnie ...

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
źródło
15

W takim przypadku, jeśli naprawdę chcesz użyć ReadToEndmetody w MemoryStreamprosty sposób, możesz użyć tej metody rozszerzenia, aby to osiągnąć:

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

Możesz użyć tej metody w następujący sposób:

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
źródło
11

Ten przykład pokazuje, jak odczytać ciąg z MemoryStream, w którym użyłem serializacji (przy użyciu DataContractJsonSerializer), przekazać ciąg z jednego serwera do klienta, a następnie, jak odzyskać MemoryStream z ciągu przekazanego jako parametr, a następnie , deserializować MemoryStream.

Użyłem części różnych postów, aby wykonać tę próbkę.

Mam nadzieję, że to pomaga.

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
źródło
5

Nieznacznie zmodyfikowana wersja odpowiedzi Briana umożliwia opcjonalne zarządzanie początkowym odczytem. Wydaje się, że jest to najłatwiejsza metoda. prawdopodobnie nie jest najbardziej wydajny, ale łatwy do zrozumienia i użycia.

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
źródło
3
to naprawdę nie dodaje nic nowego do odpowiedzi Briana
Luis Filipe
5

Dlaczego nie stworzyć ładnej metody rozszerzenia na 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;
        }
    }
}

Oczywiście należy zachować ostrożność, stosując te metody w połączeniu ze standardowymi. :) ... musisz użyć tego poręcznego streamLock, jeśli to zrobisz, do współbieżności.

Alexandru
źródło
0

Muszę zintegrować się z klasą, która potrzebuje strumienia, aby napisać na nim:

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

Tworzę prostą klasę, która może pomóc w zmniejszeniu liczby wierszy kodu dla wielokrotności:

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

następnie możesz zastąpić próbkę jednym wierszem kodu

var ret = MemoryStreamStringWrapper.Write(schema.Write);
Riccardo Bassilichi
źródło