Wie generiere ich einen Stream aus einer Zeichenfolge?

759

Ich muss einen Komponententest für eine Methode schreiben, die einen Stream verwendet, der aus einer Textdatei stammt. Ich würde gerne so etwas machen:

Stream s = GenerateStreamFromString("a,b \n c,d");
Omu
quelle
Eine Lösung zum Speichern von Speicher finden Sie StringReaderStreamunter stackoverflow.com/a/55170901/254109
xmedeko

Antworten:

956
public static Stream GenerateStreamFromString(string s)
{
    var stream = new MemoryStream();
    var writer = new StreamWriter(stream);
    writer.Write(s);
    writer.Flush();
    stream.Position = 0;
    return stream;
}

Vergessen Sie nicht, Using zu verwenden:

using (var stream = GenerateStreamFromString("a,b \n c,d"))
{
    // ... Do stuff to stream
}

Über das StreamWriternicht entsorgt werden. StreamWriterist nur ein Wrapper um den Basis-Stream und verwendet keine Ressourcen, die entsorgt werden müssen. Die DisposeMethode schließt den Basiswert Stream, in den geschrieben StreamWriterwird. In diesem Fall MemoryStreammöchten wir das zurückgeben.

In .NET 4.5 gibt es jetzt eine Überlastung StreamWriter, die den zugrunde liegenden Stream offen hält, nachdem der Writer entsorgt wurde. Dieser Code macht jedoch dasselbe und funktioniert auch mit anderen .NET-Versionen.

Siehe Gibt es eine Möglichkeit, einen StreamWriter zu schließen, ohne seinen BaseStream zu schließen?

Cameron MacFarland
quelle
134
Ein wichtiges Punktkonzept ist, dass ein Stream aus Bytes besteht, während eine Zeichenfolge aus Zeichen besteht. Es ist wichtig zu verstehen, dass beim Konvertieren eines Zeichens in ein oder mehrere Bytes (oder in einen Stream wie in diesem Fall) immer eine bestimmte Codierung verwendet wird (oder angenommen wird). Diese Antwort ist zwar in einigen Fällen korrekt, verwendet jedoch die Standardcodierung und ist möglicherweise im Allgemeinen nicht geeignet. Die explizite Übergabe einer Codierung an den StreamWriter-Konstruktor würde deutlicher machen, dass der Autor die Auswirkungen der Codierung berücksichtigen muss.
Drwatsoncode
6
Sie sagen "Vergessen Sie nicht, das Using zu verwenden", um den Stream zu verwenden, aber in Ihrer GenerateStreamFromStringMethode verwenden Sie das Using nicht mit dem StreamWriter. Gibt es einen Grund dafür?
Ben
12
@ Ben Ja. Wenn Sie den StreamWriter entsorgen, wird auch der zugrunde liegende Stream geschlossen. Das wollen wir nicht. Der einzige Grund, warum der Writer verfügbar ist, besteht darin, den Stream zu bereinigen, sodass er sicher ignoriert werden kann.
Cameron MacFarland
2
Es sollte auch beachtet werden, dass die gesamte Zeichenfolge in einen Speicher kopiert wird, was für große Zeichenfolgen wichtig sein kann, da wir jetzt eine zusätzliche Kopie im Speicher haben.
UGEEN
1
@ahong Nicht wirklich. StreamWritermacht wahrscheinlich sowieso das, was du intern gesagt hast. Der Vorteil ist die Kapselung und der einfachere Code, jedoch auf Kosten der Abstraktion von Dingen wie dem Wegcodieren. Es hängt davon ab, was Sie erreichen wollen.
Cameron MacFarland
724

Eine andere Lösung:

public static MemoryStream GenerateStreamFromString(string value)
{
    return new MemoryStream(Encoding.UTF8.GetBytes(value ?? ""));
}
Joelnet
quelle
31
Nur für den Fall, dass jemand dies mit einer XML-String-Deserialisierung verwendet, musste ich UTF8 auf Unicode umstellen, damit es ohne Flag funktioniert. Guter Eintrag!!!
Gaspa79
2
Ich mag diesen (mit Rhyous 'Tweak und dem trivialen zusätzlichen Zucker zur Verwendung als Erweiterungsmethode) besser als die akzeptierte Antwort; flexibler, weniger LOC und weniger betroffene Objekte (keine explizite Notwendigkeit für einen StreamWriter)
KeithS
2
new MemoryStream(Encoding.UTF8.GetBytes("\ufeff" + (value ?? ""))wenn Sie die Stückliste am Anfang des Streams haben müssen
robert4
5
Dies ist eine sehr kompakte Syntax, die jedoch viele Byte-Zuweisungen [] verursachen wird. Achten Sie daher auf Hochleistungscode.
michael.aird
1
Diese Lösung ließ immer noch die Möglichkeit, den Stream schreibgeschützt zu machen. new MemoryStream( value, false ). Sie können einen Stream nicht schreibgeschützt machen, wenn Sie ihn mit einem Stream-Writer schreiben müssen.
Codekandis
106

Fügen Sie dies einer Dienstprogrammklasse für statische Zeichenfolgen hinzu:

public static Stream ToStream(this string str)
{
    MemoryStream stream = new MemoryStream();
    StreamWriter writer = new StreamWriter(stream);
    writer.Write(str);
    writer.Flush();
    stream.Position = 0;
    return stream;
}

Dies fügt eine Erweiterungsfunktion hinzu, so dass Sie einfach:

using (var stringStream = "My string".ToStream())
{
    // use stringStream
}
Josh G.
quelle
5
Ich habe festgestellt, dass der zurückgegebene Stream geschlossen wird (was zu zufälligen Ausnahmen führt), wenn der Garbage Collector den Computer bereinigt StreamWriter. Das Update bestand darin, einen anderen Konstruktor zu verwenden - einen, mit dem ich LeaveOpen angeben konnte .
Bevan
45
public Stream GenerateStreamFromString(string s)
{
    return new MemoryStream(Encoding.UTF8.GetBytes(s));
}
Hexenmeister
quelle
24

Verwenden Sie die MemoryStreamKlasse und rufen Sie Encoding.GetBytesauf, um Ihre Zeichenfolge zuerst in ein Array von Bytes umzuwandeln.

Benötigen Sie anschließend eine TextReaderim Stream? In diesem Fall können Sie eine StringReaderdirekt angeben MemoryStreamund die EncodingSchritte und umgehen .

Tim Robinson
quelle
23

Ich habe eine Mischung aus Antworten wie diesen verwendet:

public static Stream ToStream(this string str, Encoding enc = null)
{
    enc = enc ?? Encoding.UTF8;
    return new MemoryStream(enc.GetBytes(str ?? ""));
}

Und dann benutze ich es so:

String someStr="This is a Test";
Encoding enc = getEncodingFromSomeWhere();
using (Stream stream = someStr.ToStream(enc))
{
    // Do something with the stream....
}
Robocide
quelle
Thomas, warum abstimmen? enc = enc ?? Encoding.UTF8 ermöglicht es mir, Streams mit einer bestimmten Codierung oder einem Standardwert von UTF8 gezielt abzufragen, und da Sie in .net (soweit ich .net 4.0 verwende) keinem anderen Referenztyp als einem String einen Standardwert in der Funktion zuweisen können Unterschrift diese Zeile ist notwendig, macht das Sinn?
Robocide
Zu erwähnen, dass Sie dies in eine separate Klasse (nicht generische statische Klasse?) einordnen müssen, ist ebenfalls hilfreich und reduziert die Abstimmungen.
Ali
13

Wir verwenden die unten aufgeführten Erweiterungsmethoden. Ich denke, Sie sollten den Entwickler dazu bringen, eine Entscheidung über die Codierung zu treffen, damit weniger Magie involviert ist.

public static class StringExtensions {

    public static Stream ToStream(this string s) {
        return s.ToStream(Encoding.UTF8);
    }

    public static Stream ToStream(this string s, Encoding encoding) {
        return new MemoryStream(encoding.GetBytes(s ?? ""));
    }
}
Shaun Bowe
quelle
1
Ich würde es vorziehen, die erste Methode als zu implementieren return ToStream(s, Encoding.UTF8);. In der aktuellen Implementierung ( return s.ToStream(Encoding.UTF8);ist der Entwickler gezwungen, s == nullgenauer nachzudenken, um den Code zu erfassen, und es scheint, dass der Fall von unbehandelt ist und wirft NullReferenceException.
Palec
10

Bitte schön:

private Stream GenerateStreamFromString(String p)
{
    Byte[] bytes = UTF8Encoding.GetBytes(p);
    MemoryStream strm = new MemoryStream();
    strm.Write(bytes, 0, bytes.Length);
    return strm;
}
cjk
quelle
1
Die Position muss nach dem Schreiben zurückgesetzt werden. Verwenden Sie besser den Konstruktor, wie in Joelnets Antwort.
Jim Balter
10

Modernisierte und leicht modifizierte Version der Erweiterungsmethoden für ToStream:

public static Stream ToStream(this string value) => ToStream(value, Encoding.UTF8);

public static Stream ToStream(this string value, Encoding encoding) 
                          => new MemoryStream(encoding.GetBytes(value ?? string.Empty));

Änderung wie in @ Palecs Kommentar zur Antwort von @Shaun Bowe vorgeschlagen.

Nick N.
quelle
8

Ich denke, Sie können von der Verwendung eines MemoryStream profitieren . Sie können es mit den Zeichenfolgenbytes füllen, die Sie mithilfe der GetBytes- Methode der Encoding-Klasse erhalten .

Konamiman
quelle
4

Wenn Sie die Codierung ändern müssen, stimme ich für die Lösung von @ShaunBowe . Aber jede Antwort hier kopiert die gesamte Zeichenfolge mindestens einmal im Speicher. Die Antworten mit ToCharArray+ BlockCopyCombo machen es zweimal.

Wenn das wichtig ist, gibt es hier einen einfachen StreamWrapper für den rohen UTF-16-String. Bei Verwendung mit einer StreamReaderAuswahl Encoding.Unicodedafür:

public class StringStream : Stream
{
    private readonly string str;

    public override bool CanRead => true;
    public override bool CanSeek => true;
    public override bool CanWrite => false;
    public override long Length => str.Length * 2;
    public override long Position { get; set; } // TODO: bounds check

    public StringStream(string s) => str = s ?? throw new ArgumentNullException(nameof(s));

    public override long Seek(long offset, SeekOrigin origin)
    {
        switch (origin)
        {
            case SeekOrigin.Begin:
                Position = offset;
                break;
            case SeekOrigin.Current:
                Position += offset;
                break;
            case SeekOrigin.End:
                Position = Length - offset;
                break;
        }

        return Position;
    }

    private byte this[int i] => (i & 1) == 0 ? (byte)(str[i / 2] & 0xFF) : (byte)(str[i / 2] >> 8);

    public override int Read(byte[] buffer, int offset, int count)
    {
        // TODO: bounds check
        var len = Math.Min(count, Length - Position);
        for (int i = 0; i < len; i++)
            buffer[offset++] = this[(int)(Position++)];
        return (int)len;
    }

    public override int ReadByte() => Position >= Length ? -1 : this[(int)Position++];
    public override void Flush() { }
    public override void SetLength(long value) => throw new NotSupportedException();
    public override void Write(byte[] buffer, int offset, int count) => throw new NotSupportedException();
    public override string ToString() => str; // ;)     
}

Und hier ist eine vollständigere Lösung mit den erforderlichen gebundenen Prüfungen (abgeleitet von MemoryStreamdiesen ToArrayund WriteToMethoden).

György Kőszeg
quelle
-2

Eine gute Kombination von String-Erweiterungen:

public static byte[] GetBytes(this string str)
{
    byte[] bytes = new byte[str.Length * sizeof(char)];
    System.Buffer.BlockCopy(str.ToCharArray(), 0, bytes, 0, bytes.Length);
    return bytes;
}

public static Stream ToStream(this string str)
{
    Stream StringStream = new MemoryStream();
    StringStream.Read(str.GetBytes(), 0, str.Length);
    return StringStream;
}
MarkWalls
quelle