Як отримати рядок із MemoryStream?


532

Якщо мені видають те, на MemoryStreamяке я знаю, що воно заповнене знаком "a" String, як я можу Stringповернутись назад?


1
Ніколи не впевнений, чи завжди потрібен читач.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 не викликається, коли змінна виходить за межі області. Фіналізація буде викликана, коли GC обійдеться до неї, але Dispose - це те, що потрібно викликати, перш ніж змінна не вийде за межі області. Я не називаю це вище, оскільки знаю, що реалізація StreamWriter та StreamReader не вимагають виклику Dispose, він просто передає виклик базовому потоку. Однак, можна викликати законний аргумент для виклику Dipose для всього, що реалізує IDisposable, оскільки ви не можете гарантувати, що майбутній випуск не вимагатиме його розпорядження.
Брайан

12
@MichaelEakins Чому відповідь має бути навіть на C #, коли питання позначено VB.Net?
Rowland Shaw

1
Я радий, що дізнався про «помічників», що передавали виклик утилізації до їхніх потоків, але це здається поганим дизайнерським рішенням.
Джерард ONeill

2
Це рішення було пом'якшено пізніше: msdn.microsoft.com/en-us/library/…
Марк Совул

310

Ви також можете використовувати

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

Я не думаю, що це менш ефективно, але я не міг би присягнути. Це також дозволяє вибрати інше кодування, тоді як, використовуючи StreamReader, вам доведеться вказати це як параметр.


6
Кодування знаходиться в просторі імен
System.Text

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/…
Skalli

99

Використання StreamReader для перетворення MemoryStream в 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

3
Встановлення позиції 0 обмежує можливість повторного використання методу - найкраще дозволити абоненту керувати цим. Що робити, якщо потік містить дані перед рядком, що абонент вміє обробляти?
Алекс Лиман

1
Використовуючи оператор гарантує, що ваш StreamReader буде видалений, але в документації йдеться про те, що StreamReader закриває базовий потік, коли він буде видалений. Таким чином, ваш метод закриває MemoryStream, який він передає, що концептуально не охолоджує абонентів, навіть якщо я сумніваюся, що MemoryStream.Dispose робить багато.
Трилліан

Ви праві. Зазвичай поганою ідеєю є використання методу Dispose для класів помічників потоку, особливо якщо потік передається в метод як параметр. Я оновлю цю відповідь. Я також маю більш повну відповідь нижче.
Брайан

Якщо ви декомпілюєте ці класи, ви побачите, що метод dispose просто викликає Dispose () у будь-яких потоках, які не є нульовими в екземплярі (TextWriter, MemoryStream тощо)
Sinaesthetic


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);
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.