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.