web-dev-qa-db-fra.com

StringStream en C #

Je veux pouvoir construire une chaîne à partir d'une classe que je crée qui dérive de Stream. Plus précisément, je veux pouvoir écrire du code comme celui-ci:

void Print(Stream stream) {
    // Some code that operates on a Stream.
}

void Main() {
    StringStream stream = new StringStream();
    Print(stream);
    string myString = stream.GetResult();
}

Puis-je créer une classe appelée StringStream qui rend cela possible? Ou une telle classe est-elle déjà disponible?

pdate: Dans mon exemple, la méthode Print est fournie dans une DLL externe tierce. Comme vous pouvez le voir, l'argument que Print attend est un Stream. Après avoir imprimé dans le Stream, je veux pouvoir récupérer son contenu sous forme de chaîne.

44
AndreyAkinshin

Vous pouvez utiliser un tandem de classes MemoryStream et StreamReader :

void Main()
{
    string myString;

    using (var stream = new MemoryStream())
    {
        Print(stream);

        stream.Position = 0;
        using (var reader = new StreamReader(stream))
        {
            myString = reader.ReadToEnd();
        }
    }
}
37
Lu55

Étant donné que votre méthode Print () traite vraisemblablement des données texte, pourriez-vous la réécrire pour accepter un paramètre TextWriter?

La bibliothèque fournit un StringWriter: TextWriter mais pas un StringStream. Je suppose que vous pouvez en créer un en encapsulant un MemoryStream, mais est-ce vraiment nécessaire?


Après la mise à jour:

void Main() 
{
  string myString;  // outside using

  using (MemoryStream stream = new MemoryStream ())
  {
     Print(stream);
     myString = Encoding.UTF8.GetString(stream.ToArray());
  }
  ... 

}

Vous souhaiterez peut-être changer UTF8 en ASCII, selon l'encodage utilisé par Print ().

35
Henk Holterman

Vous pouvez utiliser un StringWriter pour écrire des valeurs dans une chaîne. Il fournit une syntaxe de type flux (bien qu'elle ne dérive pas de Stream) qui fonctionne avec un StringBuilder sous-jacent.

12
Reed Copsey

Vous pouvez créer un MemoryStream à partir d'une chaîne et l'utiliser dans n'importe quelle fonction tierce qui nécessite un flux. Dans ce cas, MemoryStream, avec l'aide de UTF8.GetBytes, fournit les fonctionnalités de StringStream de Java.

À partir d'exemples de chaîne

String content = "stuff";
using (MemoryStream stream = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(content)))
{
    Print(stream); //or whatever action you need to perform with the stream
    stream.Seek(0, SeekOrigin.Begin); //If you need to use the same stream again, don't forget to reset it.
    UseAgain(stream);
}

Exemple de chaîne

stream.Seek(0, SeekOrigin.Begin);
string s;
using (var readr = new StreamReader(stream))
{
    s = readr.ReadToEnd();
}
//and don't forget to dispose the stream if you created it
4
NH.

Je vois beaucoup de bonnes réponses ici, mais aucune qui ne corrige directement le manque d'une classe StringStream en C #. J'ai donc écrit l'un des miens ...

public class StringStream : Stream
{
    private readonly MemoryStream _memory;
    public StringStream(string text)
    {
        _memory = new MemoryStream(Encoding.UTF8.GetBytes(text));
    }
    public StringStream()
    {
        _memory = new MemoryStream();
    }
    public StringStream(int capacity)
    {
        _memory = new MemoryStream(capacity);
    }
    public override void Flush()
    {
        _memory.Flush();
    }
    public override int Read(byte[] buffer, int offset, int count)
    {
        return  _memory.Read(buffer, offset, count);
    }
    public override long Seek(long offset, SeekOrigin Origin)
    {
        return _memory.Seek(offset, Origin);
    }
    public override void SetLength(long value)
    {
        _memory.SetLength(value);
    }
    public override void Write(byte[] buffer, int offset, int count)
    {
        _memory.Write(buffer, offset, count);
        return;
    }
    public override bool CanRead => _memory.CanRead;
    public override bool CanSeek => _memory.CanSeek;
    public override bool CanWrite => _memory.CanWrite;
    public override long Length =>  _memory.Length;
    public override long Position
    {
        get => _memory.Position;
        set => _memory.Position = value;
    }
    public override string ToString()
    {
        return System.Text.Encoding.UTF8.GetString(_memory.GetBuffer(), 0, (int) _memory.Length);
    }
    public override int ReadByte()
    {
        return _memory.ReadByte();
    }
    public override void WriteByte(byte value)
    {
        _memory.WriteByte(value);
    }
}

Un exemple de son utilisation ...

        string s0 =
            "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor\r\n" +
            "incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud\r\n" +
            "exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor\r\n" +
            "in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint\r\n" +
            "occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\r\n";
        StringStream ss0 = new StringStream(s0);
        StringStream ss1 = new StringStream();
        int line = 1;
        Console.WriteLine("Contents of input stream: ");
        Console.WriteLine();
        using (StreamReader reader = new StreamReader(ss0))
        {
            using (StreamWriter writer = new StreamWriter(ss1))
            {
                while (!reader.EndOfStream)
                {
                    string s = reader.ReadLine();
                    Console.WriteLine("Line " + line++ + ": " + s);
                    writer.WriteLine(s);
                }
            }
        }

        Console.WriteLine();
        Console.WriteLine("Contents of output stream: ");
        Console.WriteLine();
        Console.Write(ss1.ToString());
3
AQuirky

Vous avez plusieurs options:

La première consiste à ne pas utiliser de flux, mais à utiliser le TextWriter

   void Print(TextWriter writer) 
   {
   }

   void Main() 
  {
    var textWriter = new StringWriter();
    Print(writer);
    string myString = textWriter.ToString();
   }

Il est probable que TextWriter soit le niveau d'abstraction approprié pour votre fonction print. Les flux visent à écrire des données binaires, tandis que TextWriter fonctionne à un niveau d'abstraction plus élevé, spécifiquement orienté vers la sortie de chaînes.

Si votre motivation est que vous souhaitez également que votre fonction Print écrive dans des fichiers, vous pouvez également obtenir un rédacteur de texte à partir d'un flux de fichiers.

void Print(TextWriter writer) 
{
}

void PrintToFile(string filePath) 
{
     using(var textWriter = new StreamWriter(filePath))
     {
         Print(writer);
     }
}

Si vous voulez VRAIMENT un flux, vous pouvez regarder MemoryStream.

2
Andrew Shepherd