Como você obtém uma string de um MemoryStream?

532

Se eu receber um MemoryStreamque eu sei que foi preenchido com um String, como faço Stringpara voltar atrás?

Brian
fonte
1
Nunca tenha certeza se o reader.close é sempre necessário. Eu tive problemas no passado e, como regra, sempre faço isso apenas por segurança.
Crusty

Respostas:

468

Este exemplo mostra como ler e gravar uma seqüência de caracteres em um 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
fonte
3
Não vai descartar o StreamWriter quando a função fica fora do escopo?
Tim Keating
14
Dispose não é chamado quando uma variável sai do escopo. Finalize será chamado quando o GC chegar a ele, mas Dispose é algo que deve ser chamado antes que a variável saia do escopo. Não o chamo acima porque sei que a implementação do StreamWriter e StreamReader não exigem que Dispose seja chamado, apenas passa a chamada para o fluxo subjacente. No entanto, um argumento legítimo pode ser feito para chamar Dipose para qualquer coisa que implemente IDisposable, pois você não pode garantir que uma versão futura não exija que ela seja descartada.
Brian
12
@MichaelEakins Por que a resposta deve estar em C #, quando a pergunta está marcada como VB.Net?
Rowland Shaw
1
Estou feliz por ter aprendido sobre os "auxiliares" que passam a chamada de descarte para seus fluxos subjacentes, mas isso parece uma má decisão de design.
precisa
2
Essa decisão foi atenuada posteriormente: msdn.microsoft.com/en-us/library/…
Mark Sowul 3/16/16
310

Você também pode usar

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

Não acho que isso seja menos eficiente, mas não podia jurar. Ele também permite que você escolha uma codificação diferente, enquanto que usando um StreamReader você precisará especificar isso como parâmetro.

Coderer
fonte
6
A codificação está no namespace System.Text
northben 15/01
2
Eu estava procurando pelo equivalente do PowerShell e tive que usá-lo. ([System.Text.Encoding] :: ASCII) .GetString (ms.ToArray ())
Lewis
Esta solução é útil, pois pode ser usada após o fechamento do MemoryStream.
Jacob Horbulyk
2
FWIW, eu achei que isso não funcionava com strings muito grandes, eu estava recebendo OutOfMemoryExceptions. O uso de um StreamReaderresolvido resolveu o problema.
Grant H.
Como isso pode ser uma armadilha: não está ciente da marca de ordem dos bytes e pode incluir um hex 00no início da string. 00 3C 3F-> .<?no Editor Hex mas em VS ou Notepad ++: <?. Portanto, você não pode ver a diferença, mesmo se comparar as strings a olho, apenas uma ferramenta de comparação ou um editor Hex mostrará a diferença. Se você ainda o usar, pense em String.TrimStart. Veja: docs.microsoft.com/en-us/dotnet/api/…
Skalli
99

Usando um StreamReader para converter o MemoryStream em uma 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
fonte
3
Definir a posição como 0 limita a capacidade de reutilização do método - é melhor permitir que o chamador gerencie isso. E se o fluxo contiver dados anteriores à sequência, com os quais o chamador sabe como lidar?
Alex Lyman
1
A instrução using garantirá que o StreamReader seja descartado, mas a documentação diz que o StreamReader fecha o fluxo subjacente quando é descartado. Portanto, seu método fecha o MemoryStream que é passado, o que é conceitual pouco interessante para os chamadores, mesmo que eu duvide que o MemoryStream.Dispose faça muito.
30510 Trillian
Você está certo. Normalmente, é uma má idéia usar o método Dispose nas classes auxiliares do fluxo, especialmente se o fluxo for passado para um método como parâmetro. Vou atualizar esta resposta. Eu também tenho uma resposta mais completa abaixo.
Brian Brian
Se você descompilar dessas classes, você vai ver que o método dispose simplesmente chama Dispose () em todos os fluxos que não são nulas na instância (TextWriter, MemoryStream, etc)
Sinaesthetic
39

use um StreamReader , então você pode usar o método ReadToEnd que retorna uma seqüência de caracteres.

Darren Kopp
fonte
12
Eu só quero mencionar que o Basestreamshould definiu sua posição como 0. Like 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
fonte
22

As soluções anteriores não funcionariam nos casos em que a codificação estivesse envolvida. Aqui está - como uma "vida real" - exemplo de como fazer isso corretamente ...

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
fonte
15

Nesse caso, se você realmente quiser usar o ReadToEndmétodo de MemoryStreamuma maneira fácil, poderá usar este método de extensão para conseguir isso:

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

E você pode usar este método desta maneira:

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
fonte
11

Este exemplo mostra como ler uma string de um MemoryStream, no qual eu usei uma serialização (usando DataContractJsonSerializer), passar a string de algum servidor para cliente e, em seguida, como recuperar o MemoryStream da string passada como parâmetro e, em seguida, , desserialize o MemoryStream.

Eu usei partes de postagens diferentes para executar este exemplo.

Espero que isso ajude.

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
fonte
5

Uma versão ligeiramente modificada da resposta de Brian permite o gerenciamento opcional do início da leitura. Esse parece ser o método mais fácil. provavelmente não é o mais eficiente, mas fácil de entender e usar.

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
fonte
3
ele realmente não acrescenta nada novo para Brian resposta
Luis Filipe
5

Por que não fazer um bom método de extensão no 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;
        }
    }
}

Obviamente, tenha cuidado ao usar esses métodos em conjunto com os métodos padrão. :) ... você precisará usar esse streamLock acessível, se o fizer, por simultaneidade.

Alexandru
fonte
0

Preciso me integrar a uma classe que precise de um fluxo para escrever nele:

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

Eu crio uma classe simples que pode ajudar a reduzir linhas de código para uso múltiplo:

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

então você pode substituir a amostra por uma única linha de código

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