Comment obtenir une chaîne d'un MemoryStream?

532

Si on me donne un MemoryStreamque je sais avoir été rempli avec un String, comment puis-je obtenir un Stringretour?

Brian
la source
1
Jamais tout à fait sûr si reader.close est toujours requis. J'ai eu des problèmes dans le passé, donc en règle générale, je fais toujours juste pour être du bon côté.
Crusty

Réponses:

468

Cet exemple montre comment lire et écrire une chaîne dans un 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
la source
3
Ne va-t-il pas disposer de StreamWriter quand la fonction sortira de toute façon?
Tim Keating du
14
Dispose n'est pas appelé lorsqu'une variable sort du domaine. Finalize sera appelé lorsque le GC y arrivera, mais Dispose est quelque chose qui doit être appelé avant que la variable ne soit hors de portée. Je ne l'appelle pas ci-dessus parce que je sais que la mise en œuvre de StreamWriter et StreamReader ne nécessite pas l'appel de Dispose, il passe simplement l'appel au flux sous-jacent. Cependant, un argument légitime peut être avancé pour appeler Dipose pour tout ce qui implémente IDisposable car vous ne pouvez pas garantir qu'une future version ne nécessitera pas sa suppression.
Brian
12
@MichaelEakins Pourquoi la réponse devrait-elle même être en C #, lorsque la question est étiquetée comme VB.Net?
Rowland Shaw
1
Je suis heureux d'avoir appris que les "aides" transmettaient l'appel de suppression à leurs flux sous-jacents, mais cela semble être une mauvaise décision de conception.
Gerard ONeill
2
Cette décision a été annulée
Mark Sowul
310

Vous pouvez aussi utiliser

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

Je ne pense pas que ce soit moins efficace, mais je ne pouvais pas le jurer. Il vous permet également de choisir un encodage différent, alors qu'en utilisant un StreamReader, vous devrez le spécifier en tant que paramètre.

Coderer
la source
6
L'encodage est dans l'espace de noms System.Text
northben
2
Je cherchais l'équivalent PowerShell de cela et je devais l'utiliser. ([System.Text.Encoding] :: ASCII) .GetString (ms.ToArray ())
Lewis
Cette solution est utile car elle peut être utilisée après la fermeture de MemoryStream.
Jacob Horbulyk
2
FWIW, j'ai trouvé que cela ne fonctionnait pas avec de très grandes cordes, je recevais OutOfMemoryExceptions. Utiliser à la StreamReaderplace a résolu le problème.
Grant H.
Étant donné que cela peut être un piège: il ne connaît pas la marque d'ordre des octets et peut inclure un hexadécimal 00au début de la chaîne. 00 3C 3F-> .<?dans Hex Editor mais dans VS ou Notepad ++: <?. Donc, vous ne pouvez pas voir la différence même si vous comparez les chaînes à l'œil nu, seul un outil de comparaison ou un éditeur hexadécimal montrera la différence. Si vous l'utilisez toujours, pensez à String.TrimStart. Voir: docs.microsoft.com/en-us/dotnet/api/…
Skalli
99

Utilisation d'un StreamReader pour convertir le MemoryStream en une chaîne.

<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
la source
3
Définir la position sur 0 limite la capacité de réutilisation de la méthode - il est préférable de laisser l'appelant gérer cela. Que se passe-t-il si le flux contient des données avant la chaîne que l'appelant sait comment gérer?
Alex Lyman
1
L'instruction using garantira que votre StreamReader sera supprimé, mais la documentation indique que StreamReader ferme le flux sous-jacent lorsqu'il est supprimé. Par conséquent, votre méthode ferme le MemoryStream qu'il est passé, ce qui est conceptuellement peu cool pour les appelants même si je doute que MemoryStream.Dispose fasse beaucoup.
Trillian
Vous avez raison. C'est généralement une mauvaise idée d'utiliser la méthode Dispose sur les classes d'assistance de flux, surtout si le flux est passé dans une méthode en tant que paramètre. Je mettrai à jour cette réponse. J'ai également une réponse plus complète ci-dessous.
Brian
Si vous décompilez ces classes, vous verrez que la méthode dispose appelle simplement Dispose () sur tous les flux qui ne sont pas nuls dans l'instance (TextWriter, MemoryStream, etc.)
Sinaesthetic
39

utilisez un StreamReader , vous pouvez alors utiliser la méthode ReadToEnd qui renvoie une chaîne.

Darren Kopp
la source
12
Je veux juste mentionner que le Basestreamdevrait avoir mis sa position à 0. Comme 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
la source
22

Les solutions précédentes ne fonctionneraient pas dans les cas où l'encodage est impliqué. Voici - une sorte de "vraie vie" - par exemple comment faire cela correctement ...

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
la source
15

Dans ce cas, si vous voulez vraiment utiliser la ReadToEndméthode de MemoryStreammanière simple, vous pouvez utiliser cette méthode d'extension pour y parvenir:

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

Et vous pouvez utiliser cette méthode de cette manière:

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
la source
11

Cet exemple montre comment lire une chaîne à partir d'un MemoryStream, dans lequel j'ai utilisé une sérialisation (à l'aide de DataContractJsonSerializer), transmettre la chaîne d'un serveur à un client, puis comment récupérer le MemoryStream à partir de la chaîne passée en paramètre, puis , désérialisez le MemoryStream.

J'ai utilisé des parties de différents articles pour effectuer cet exemple.

J'espère que cela vous aidera.

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
la source
5

Une version légèrement modifiée de la réponse de Brian permet une gestion facultative du démarrage de la lecture. Cela semble être la méthode la plus simple. probablement pas le plus efficace, mais facile à comprendre et à utiliser.

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
la source
3
cela n'ajoute vraiment rien de nouveau à la réponse de Brian
Luis Filipe
5

Pourquoi ne pas faire une belle méthode d'extension sur le type 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;
        }
    }
}

Bien sûr, soyez prudent lorsque vous utilisez ces méthodes en conjonction avec les méthodes standard. :) ... vous devrez utiliser ce streamLock pratique si vous le faites, pour la concurrence.

Alexandru
la source
0

J'ai besoin de m'intégrer à une classe qui a besoin d'un Stream pour écrire dessus:

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

Je crée une classe simple qui peut aider à réduire les lignes de code pour une utilisation multiple:

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

alors vous pouvez remplacer l'exemple par une seule ligne de code

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