Bagaimana cara menghasilkan aliran dari string?

759

Saya perlu menulis unit test untuk metode yang mengambil aliran yang berasal dari file teks. Saya ingin melakukan sesuatu seperti ini:

Stream s = GenerateStreamFromString("a,b \n c,d");
Omu
sumber
Untuk solusi penghematan memori, lihat StringReaderStreamdi stackoverflow.com/a/55170901/254109
xmedeko

Jawaban:

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

Jangan lupa untuk menggunakan Menggunakan:

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

Tentang StreamWritertidak dibuang. StreamWriterhanyalah pembungkus di sekitar aliran dasar, dan tidak menggunakan sumber daya apa pun yang perlu dibuang. The DisposeMetode akan menutup mendasari Streamyang StreamWritermenulis untuk. Dalam hal ini MemoryStreamkita ingin kembali.

Di .NET 4.5 sekarang ada kelebihan untuk StreamWriteryang membuat aliran yang mendasari terbuka setelah penulis dibuang, tetapi kode ini melakukan hal yang sama dan bekerja dengan versi lain dari .NET juga.

Lihat Apakah ada cara untuk menutup StreamWriter tanpa menutup BaseStream-nya?

Cameron MacFarland
sumber
134
Konsep poin penting untuk ditunjukkan adalah aliran terdiri dari byte, sedangkan string terdiri dari karakter. Sangat penting untuk memahami bahwa mengubah karakter menjadi satu atau lebih byte (atau ke Stream seperti dalam kasus ini) selalu menggunakan (atau mengasumsikan) pengkodean tertentu. Jawaban ini, meskipun benar dalam beberapa kasus, menggunakan pengodean Default, dan mungkin tidak cocok secara umum. Melewatkan Encoding ke konstruktor StreamWriter secara eksplisit akan membuatnya lebih jelas bahwa penulis perlu mempertimbangkan implikasi Encoding.
drwatsoncode
6
Anda mengatakan "Jangan lupa untuk menggunakan Menggunakan" untuk menggunakan aliran, tetapi dalam GenerateStreamFromStringmetode Anda Anda tidak menggunakan Menggunakan dengan StreamWriter. Apakah ada alasan untuk ini?
Ben
12
@ Ben Ya. Jika Anda membuang StreamWriter, aliran yang mendasarinya juga akan ditutup. Kami tidak menginginkan itu. Satu-satunya alasan Writer adalah sekali pakai adalah untuk membersihkan aliran, sehingga aman untuk diabaikan.
Cameron MacFarland
2
Perlu juga dicatat, bahwa seluruh string disalin ke memori yang mungkin penting untuk string besar karena sekarang kita memiliki satu salinan tambahan dalam memori.
UGEEN
1
@ ahong Tidak juga. StreamWritermungkin melakukan apa yang Anda katakan secara internal. Keuntungannya adalah enkapsulasi dan kode sederhana, tetapi dengan mengorbankan abstrak hal-hal seperti penyandian. Itu tergantung pada apa yang ingin Anda capai.
Cameron MacFarland
724

Solusi lain:

public static MemoryStream GenerateStreamFromString(string value)
{
    return new MemoryStream(Encoding.UTF8.GetBytes(value ?? ""));
}
joelnet
sumber
31
Hanya dalam kasus seseorang menggunakan ini dengan deserialization string XML, saya harus beralih UTF8 ke Unicode agar berfungsi tanpa bendera. Pos yang bagus !!!
Gaspa79
2
Saya suka yang ini (dengan tweak Rhyous dan tambahan gula sepele untuk digunakan sebagai metode ekstensi) lebih baik daripada jawaban yang diterima; lebih fleksibel, lebih sedikit LOC dan lebih sedikit objek yang terlibat (tidak perlu eksplisit untuk StreamWriter)
KeithS
2
new MemoryStream(Encoding.UTF8.GetBytes("\ufeff" + (value ?? ""))jika Anda perlu untuk memasukkan BOM pada awal aliran
robert4
5
Ini sintaks yang sangat kompak tetapi akan menyebabkan banyak alokasi byte [] jadi berhati-hatilah dalam kode kinerja tinggi.
michael.aird
1
Solusi ini masih meninggalkan peluang untuk membuat streaming hanya dibaca. new MemoryStream( value, false ). Anda tidak dapat membuat aliran dibaca hanya jika Anda harus menulisnya dengan penulis aliran.
codekandis
106

Tambahkan ini ke kelas utilitas string statis:

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

Ini menambahkan fungsi ekstensi sehingga Anda dapat:

using (var stringStream = "My string".ToStream())
{
    // use stringStream
}
Josh G
sumber
5
Saya menemukan bahwa aliran balik ditutup (menyebabkan pengecualian semi-acak) ketika pengumpul sampah membersihkan StreamWriter. Cara mengatasinya adalah menggunakan konstruktor yang berbeda - yang memungkinkan saya untuk menentukan leaveOpen .
Bevan
45
public Stream GenerateStreamFromString(string s)
{
    return new MemoryStream(Encoding.UTF8.GetBytes(s));
}
Ahli sihir
sumber
24

Gunakan MemoryStreamkelas, panggil Encoding.GetBytesuntuk mengubah string Anda menjadi array byte terlebih dahulu.

Apakah Anda selanjutnya memerlukan TextReaderon the stream? Jika demikian, Anda bisa menyediakan secara StringReaderlangsung, dan memotong MemoryStreamdan Encodinglangkah - langkah.

Tim Robinson
sumber
23

Saya menggunakan campuran jawaban seperti ini:

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

Dan kemudian saya menggunakannya seperti ini:

String someStr="This is a Test";
Encoding enc = getEncodingFromSomeWhere();
using (Stream stream = someStr.ToStream(enc))
{
    // Do something with the stream....
}
Robocide
sumber
Thomas, mengapa memilih? enc = enc ?? Encoding.UTF8 memungkinkan saya untuk secara spesifik menanyakan aliran dengan pengkodean tertentu, atau default UTF8, dan karena dalam .net (sejauh ini saya menggunakannya .net 4.0) Anda tidak dapat memberikan tipe referensi selain string nilai default dalam fungsi tanda tangani baris ini perlu, apakah itu masuk akal?
Robocide
menyebutkan bahwa Anda perlu meletakkan ini di kelas yang terpisah (kelas statis non generik?) juga membantu dan mengurangi suara turun.
Ali
13

Kami menggunakan metode ekstensi yang tercantum di bawah ini. Saya pikir Anda harus membuat pengembang membuat keputusan tentang pengkodean, jadi ada sedikit keajaiban yang terlibat.

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
sumber
1
Saya lebih suka menerapkan metode pertama sebagai return ToStream(s, Encoding.UTF8);. Dalam implementasi saat ini ( return s.ToStream(Encoding.UTF8);, pengembang dipaksa untuk berpikir lebih keras untuk memahami kode dan tampaknya kasus s == nulltidak tertangani dan melempar NullReferenceException.
Palec
10

Ini dia:

private Stream GenerateStreamFromString(String p)
{
    Byte[] bytes = UTF8Encoding.GetBytes(p);
    MemoryStream strm = new MemoryStream();
    strm.Write(bytes, 0, bytes.Length);
    return strm;
}
cjk
sumber
1
Posisi perlu diatur ulang setelah menulis. Lebih baik menggunakan konstruktor, seperti dalam jawaban joelnet.
Jim Balter
10

Versi modern dari metode ekstensi untuk 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));

Modifikasi seperti yang disarankan dalam komentar @ Palec tentang jawaban @Shaun Bowe.

Nick N.
sumber
8

Saya pikir Anda bisa mendapat manfaat dari menggunakan MemoryStream . Anda bisa mengisinya dengan byte string yang Anda peroleh dengan menggunakan metode GetBytes dari kelas Encoding .

Konamiman
sumber
4

Jika Anda perlu mengubah penyandian, saya memilih solusi @ShaunBowe . Tetapi setiap jawaban di sini menyalin seluruh string dalam memori setidaknya sekali. Jawaban dengan ToCharArray+ BlockCopykombo melakukannya dua kali.

Jika yang penting di sini adalah Streampembungkus sederhana untuk string UTF-16 mentah. Jika digunakan dengan StreamReaderpilih Encoding.Unicodeuntuk itu:

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

Dan di sini adalah solusi yang lebih lengkap dengan cek terikat yang diperlukan (berasal dari MemoryStreamsehingga memiliki ToArraydan WriteTometode juga).

György Kőszeg
sumber
-2

Kombinasi ekstensi String yang baik:

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
sumber