Jak wygenerować strumień z ciągu?


Odpowiedzi:


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

Nie zapomnij użyć przy użyciu:

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

O StreamWriterniezbywaniu. StreamWriterjest tylko opakowaniem wokół strumienia podstawowego i nie wykorzystuje żadnych zasobów, które należy usunąć. DisposeMetoda zamknie instrumentu bazowego Stream, który StreamWriterpisze do. W takim przypadku MemoryStreamchcemy wrócić.

W .NET 4.5 występuje teraz przeciążenie, StreamWriterktóre utrzymuje otwarty strumień źródłowy po usunięciu programu piszącego, ale ten kod robi to samo i działa również z innymi wersjami .NET.

Zobacz Czy jest jakiś sposób na zamknięcie StreamWriter bez zamykania BaseStream?


134
Ważną koncepcją punktową, na którą należy zwrócić uwagę, jest to, że strumień składa się z bajtów, a łańcuch składa się ze znaków. Ważne jest, aby zrozumieć, że konwersja znaku na jeden lub więcej bajtów (lub na strumień, jak w tym przypadku) zawsze używa (lub zakłada) określone kodowanie. Ta odpowiedź, choć poprawna w niektórych przypadkach, wykorzystuje kodowanie domyślne i może być ogólnie nieodpowiednia. Jawne przekazanie kodowania do konstruktora StreamWriter sprawi, że bardziej oczywiste będzie, że autor musi rozważyć konsekwencje kodowania.
drwatsoncode

6
Mówisz „Nie zapomnij użyć przy użyciu” do korzystania ze strumienia, ale w swojej GenerateStreamFromStringmetodzie nie używasz przy użyciu przy użyciu StreamWriter. Czy jest tego powód?
Ben

12
@Ben Tak. Jeśli pozbędziesz się StreamWriter, strumień również zostanie zamknięty. Nie chcemy tego. Jedynym powodem, dla którego Writer jest jednorazowy, jest czyszczenie strumienia, więc można go zignorować.
Cameron MacFarland

2
Należy również zauważyć, że cały ciąg jest kopiowany do pamięci, co może być ważne dla dużych ciągów, ponieważ teraz mamy jedną dodatkową kopię w pamięci.
UGEEN

1
@ahong Nie bardzo. StreamWriteri tak prawdopodobnie robi to, co powiedziałeś wewnętrznie. Zaletą jest enkapsulacja i prostszy kod, ale kosztem abstrakcji takich rzeczy, jak kodowanie. To zależy od tego, co próbujesz osiągnąć.
Cameron MacFarland

724

Inne rozwiązanie:

public static MemoryStream GenerateStreamFromString(string value)
{
    return new MemoryStream(Encoding.UTF8.GetBytes(value ?? ""));
}

31
Na wypadek, gdyby ktoś użył tego z deserializacją łańcucha XML, musiałem przełączyć UTF8 na Unicode, aby działał bez flagi. Wspaniały post!!!
Gaspa79

2
Podoba mi się ten (z poprawką Rhyousa i trywialnym dodatkowym cukrem do stosowania jako metoda rozszerzenia) lepiej niż zaakceptowana odpowiedź; bardziej elastyczny, mniej LOC i mniej zaangażowanych obiektów (nie ma wyraźnej potrzeby StreamWriter)
KeithS

2
new MemoryStream(Encoding.UTF8.GetBytes("\ufeff" + (value ?? ""))jeśli chcesz dołączyć BOM na początku strumienia
robert4,

5
Jest to bardzo zwarta składnia, ale spowoduje wiele alokacji bajtów [], więc uważaj na kod o wysokiej wydajności.
michael.aird

1
To rozwiązanie wciąż pozostawiło okazję do odczytu strumienia. new MemoryStream( value, false ). Nie możesz zrobić strumienia tylko do odczytu, jeśli musisz napisać go za pomocą programu do zapisu strumieniowego.
codekandis,

106

Dodaj to do klasy narzędzia ciąg statyczny:

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

Dodaje to funkcję rozszerzenia, dzięki czemu możesz po prostu:

using (var stringStream = "My string".ToStream())
{
    // use stringStream
}

5
Odkryłem, że zwrócony strumień zamyka się (powodując pół losowe wyjątki), gdy moduł czyszczenia pamięci czyści plik StreamWriter. Poprawka polegała na użyciu innego konstruktora - takiego, który pozwolił mi określić parametr LeaveOpen .
Bevan

45
public Stream GenerateStreamFromString(string s)
{
    return new MemoryStream(Encoding.UTF8.GetBytes(s));
}

24

Użyj MemoryStreamklasy, wywołując, Encoding.GetBytesaby najpierw przekształcić łańcuch znaków w tablicę bajtów.

Czy potrzebujesz później TextReaderw strumieniu? Jeśli tak, możesz podać StringReaderbezpośrednio i ominąć kroki MemoryStreami Encoding.


23

Użyłem kombinacji takich odpowiedzi:

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

A potem używam tego w ten sposób:

String someStr="This is a Test";
Encoding enc = getEncodingFromSomeWhere();
using (Stream stream = someStr.ToStream(enc))
{
    // Do something with the stream....
}

Thomas, dlaczego głosować w dół? enc = enc ?? Encoding.UTF8 pozwala mi konkretnie zapytać strumień z konkretnym kodowaniem lub domyślnym UTF8, a ponieważ w .net (o ile go używam .net 4.0) nie można podać typu referencyjnego innego niż string domyślna wartość w funkcji podpis ten wiersz jest konieczny, czy to ma sens?
Robocide

wspomnienie, że musisz umieścić to w osobnej klasie (ogólna klasa statyczna?) jest również pomocne i zmniejsza liczbę głosów negatywnych.
Ali

13

Korzystamy z metod rozszerzenia wymienionych poniżej. Myślę, że powinieneś skłonić programistę do podjęcia decyzji o kodowaniu, aby w grę wchodziło mniej magii.

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 ?? ""));
    }
}

1
Wolałbym wdrożyć pierwszą metodę jako return ToStream(s, Encoding.UTF8);. W obecnej implementacji ( return s.ToStream(Encoding.UTF8);deweloper jest zmuszony do s == nullNullReferenceException
większego

10

Proszę bardzo:

private Stream GenerateStreamFromString(String p)
{
    Byte[] bytes = UTF8Encoding.GetBytes(p);
    MemoryStream strm = new MemoryStream();
    strm.Write(bytes, 0, bytes.Length);
    return strm;
}

1
Pozycja musi zostać zresetowana po zapisaniu. Lepiej używać konstruktora, jak w odpowiedzi Joelneta.
Jim Balter,

10

Zmodernizowana i nieznacznie zmodyfikowana wersja metod rozszerzenia dla 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));

Modyfikacja sugerowana w komentarzu @ Palec do odpowiedzi @Shaun Bowe.



3

Jeśli potrzebujesz zmienić kodowanie, głosuję na rozwiązanie @ShaunBowe . Ale każda odpowiedź tutaj co najmniej raz kopiuje cały ciąg do pamięci. Odpowiedzi z kombinacją ToCharArray+ BlockCopyzrobić to dwa razy.

Jeśli to ma znaczenie, to proste Streamopakowanie na nieprzetworzony ciąg UTF-16. W przypadku użycia z StreamReaderwyborem Encoding.Unicode:

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

A oto bardziej kompletne rozwiązanie z niezbędnymi kontrolami powiązanymi (pochodzące z MemoryStreamtak ma ToArrayi WriteTometod).


-2

Dobra kombinacja rozszerzeń String:

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;
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.