Как вы получаете строку из MemoryStream?

532

Если мне дают то, MemoryStreamчто, как я знаю, было заполнено String, как я могу получить Stringответ?

Брайан
источник
1
Никогда не уверен, всегда ли требуется reader.close. У меня были проблемы в прошлом, поэтому, как правило, я всегда стараюсь быть в безопасности.
Crusty

Ответы:

468

В этом примере показано, как читать и записывать строку в 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
Разве он не собирается утилизировать StreamWriter, когда функция выходит из области видимости?
Тим Китинг
14
Dispose не вызывается, когда переменная выходит из области видимости. Finalize будет вызван, когда GC доберется до него, но Dispose - это то, что необходимо вызвать, прежде чем переменная выйдет из области видимости. Я не называю это выше, потому что я знаю, что реализация StreamWriter и StreamReader не требуют вызова Dispose, он просто передает вызов базовому потоку. Тем не менее, можно обоснованно аргументировать вызов Dipose для всего, что реализует IDisposable, поскольку вы не можете гарантировать, что будущий выпуск не потребует его утилизации.
Брайан
12
@MichaelEakins Почему ответ должен быть даже на C #, когда вопрос помечен как VB.Net?
Роуланд Шоу
1
Я рад, что узнал о «помощниках», передающих вызов dispose их базовым потокам, но это кажется плохим дизайнерским решением.
Джерард ONeill
2
Это решение было позже смягчено: msdn.microsoft.com/en-us/library/…
Марк Соул
310

Вы также можете использовать

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

Я не думаю, что это менее эффективно, но я не мог поклясться в этом. Это также позволяет вам выбрать другую кодировку, тогда как при использовании StreamReader вам нужно будет указать это в качестве параметра.

Coderer
источник
6
Кодировка находится в пространстве имен System.Text
northben
2
Я искал эквивалент PowerShell этого и должен был использовать это. ([System.Text.Encoding] :: ASCII) .GetString (ms.ToArray ())
Льюис
Это решение полезно, поскольку его можно использовать после закрытия MemoryStream.
Джейкоб Горбулык
2
FWIW, я обнаружил, что это не работает с очень большими строками, я получаю OutOfMemoryExceptions. Использование StreamReaderвместо этого решило проблему.
Грант Х.
Поскольку это может быть ловушкой: он не знает о метке порядка байтов и может включать шестнадцатеричный код 00в начале строки. 00 3C 3F-> .<?в Hex Editor , но в VS или Notepad ++: <?. Таким образом, вы не сможете увидеть разницу, даже если вы сравните строки на глаз, только инструмент сравнения или шестнадцатеричный редактор покажет разницу. Если вы все еще используете его, подумайте о String.TrimStart. См .: docs.microsoft.com/en-us/dotnet/api/…
Скалли
99

Использование StreamReader для преобразования MemoryStream в строку.

<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
Установка позиции в 0 ограничивает возможность повторного использования метода - лучше всего, чтобы вызывающая сторона управляла этим. Что если поток содержит данные до строки, которые вызывающий знает, как обрабатывать?
Алекс Лиман
1
Оператор using гарантирует удаление вашего StreamReader, но в документации говорится, что StreamReader закрывает основной поток, когда он удаляется. Следовательно, ваш метод закрывает MemoryStream, который ему передают, что концептуально не охлаждает вызывающих, даже если я сомневаюсь, что MemoryStream.Dispose делает многое.
Триллиан
Ты прав. Обычно плохая идея использовать метод Dispose для потоковых вспомогательных классов, особенно если поток передается в метод в качестве параметра. Я обновлю этот ответ. У меня также есть более полный ответ ниже.
Брайан
Если вы декомпилируете эти классы, вы увидите, что метод dispose просто вызывает Dispose () для любых потоков, которые не являются нулевыми в экземпляре (TextWriter, MemoryStream и т. Д.)
Sinaesthetic,
39

используйте StreamReader , затем вы можете использовать метод ReadToEnd, который возвращает строку.

Даррен Копп
источник
12
Я просто хочу упомянуть, что Basestreamдолжен был установить свою позицию на 0. Нравится 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

Предыдущие решения не работали в случаях, когда используется кодирование. Вот - вид "реальной жизни" - пример, как это сделать правильно ...

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

В этом случае, если вы действительно хотите использовать ReadToEndметод MemoryStreamпростым способом, вы можете использовать этот метод расширения для достижения этой цели:

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

И вы можете использовать этот метод следующим образом:

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

В этом примере показано, как прочитать строку из MemoryStream, в которой я использовал сериализацию (используя DataContractJsonSerializer), передать строку с какого-либо сервера клиенту, а затем как восстановить MemoryStream из строки, переданной в качестве параметра, затем , десериализовать MemoryStream.

Я использовал части различных постов для выполнения этого образца.

Надеюсь, что это поможет.

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

Слегка измененная версия ответа Брайана позволяет опционально управлять началом чтения. Это кажется самым простым методом. Вероятно, не самый эффективный, но простой для понимания и использования.

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
это действительно не добавляет ничего нового в ответ Брайана
Луис Филипе
5

Почему бы не создать хороший метод расширения для типа 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;
        }
    }
}

Конечно, будьте осторожны при использовании этих методов в сочетании со стандартными. :) ... вам понадобится использовать этот удобный streamLock для параллелизма.

Александр
источник
0

Мне нужно интегрироваться с классом, которому нужен поток для записи на него:

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

Я создаю простой класс, который может помочь уменьшить количество строк кода для многократного использования:

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

тогда вы можете заменить образец одной строкой кода

var ret = MemoryStreamStringWrapper.Write(schema.Write);
Риккардо Басиличи
источник