Làm cách nào để tạo luồng từ chuỗi?


759

Tôi cần phải viết một bài kiểm tra đơn vị cho một phương thức lấy một luồng phát ra từ một tệp văn bản. Tôi muốn làm một cái gì đó như thế này:

Stream s = GenerateStreamFromString("a,b \n c,d");

Để biết giải pháp tiết kiệm bộ nhớ, hãy xem StringReaderStreamtrong stackoverflow.com/a/55170901/254109
xmedeko

Câu trả lời:


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

Đừng quên sử dụng:

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

Về việc StreamWriterkhông được xử lý. StreamWriterchỉ là một trình bao bọc xung quanh luồng cơ sở và không sử dụng bất kỳ tài nguyên nào cần được xử lý. Các Disposephương pháp sẽ đóng tiềm ẩn StreamStreamWriterđược viết cho. Trong trường hợp này đó là MemoryStreamchúng tôi muốn trở lại.

Trong .NET 4.5 hiện có một tình trạng quá tải để StreamWritergiữ luồng bên dưới mở sau khi người viết bị loại bỏ, nhưng mã này cũng làm điều tương tự và cũng hoạt động với các phiên bản .NET khác.

Xem Có cách nào để đóng StreamWriter mà không đóng BaseStream không?


134
Một khái niệm điểm quan trọng cần chỉ ra là một luồng bao gồm các byte, trong khi một chuỗi bao gồm các ký tự. Điều quan trọng là phải hiểu rằng việc chuyển đổi một ký tự thành một hoặc nhiều byte (hoặc thành Luồng như trong trường hợp này) luôn sử dụng (hoặc giả định) một mã hóa cụ thể. Câu trả lời này, trong khi chính xác trong một số trường hợp, sử dụng mã hóa Mặc định và nói chung có thể không phù hợp. Rõ ràng việc truyền Mã hóa cho nhà xây dựng StreamWriter sẽ làm cho rõ ràng hơn rằng tác giả cần xem xét ý nghĩa của Mã hóa.
drwatson

6
Bạn nói "Đừng quên sử dụng" để sử dụng luồng, nhưng trong GenerateStreamFromStringphương pháp của bạn, bạn không sử dụng Sử dụng với StreamWriter. Có một lý do cho điều này?
Ben

12
@Ben Có. Nếu bạn loại bỏ StreamWriter, luồng bên dưới cũng sẽ bị đóng. Chúng tôi không muốn điều đó. Lý do duy nhất mà Nhà văn chỉ dùng một lần là để dọn sạch luồng, vì vậy an toàn để bỏ qua.
Cameron MacFarland

2
Cũng cần lưu ý rằng toàn bộ chuỗi được sao chép vào bộ nhớ có thể quan trọng đối với các chuỗi lớn vì bây giờ chúng ta có thêm một bản sao trong bộ nhớ.
UGEEN

1
@ahong Không hẳn. StreamWritercó lẽ đang làm những gì bạn nói trong nội bộ Ưu điểm là đóng gói và mã đơn giản hơn, nhưng với chi phí trừu tượng hóa những thứ như mã hóa đi. Nó phụ thuộc vào những gì bạn đang cố gắng để đạt được.
Cameron MacFarland

724

Giải pháp khác:

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

31
Chỉ trong trường hợp ai đó sử dụng điều này với việc khử tuần tự chuỗi XML, tôi đã phải chuyển UTF8 sang Unicode để nó hoạt động mà không cần cờ. Bài đăng tuyệt vời !!!
Gaspa79

2
Tôi thích cái này (với tinh chỉnh của Rhyous và đường bổ sung tầm thường để sử dụng như một phương pháp mở rộng) tốt hơn câu trả lời được chấp nhận; linh hoạt hơn, ít LỘC hơn và ít đối tượng hơn có liên quan (không cần rõ ràng về StreamWriter)
KeithS

2
new MemoryStream(Encoding.UTF8.GetBytes("\ufeff" + (value ?? ""))nếu bạn cần có BOM bao gồm ở đầu luồng
robert4

5
Đây là cú pháp rất nhỏ gọn nhưng nó sẽ gây ra nhiều phân bổ byte [] vì vậy hãy cẩn thận với mã hiệu suất cao.
michael.aird

1
Giải pháp này vẫn để lại cơ hội để làm cho dòng chỉ đọc. new MemoryStream( value, false ). Bạn không thể tạo một luồng chỉ đọc nếu bạn phải viết nó với một trình soạn thảo luồng.
codekandis

106

Thêm phần này vào một lớp tiện ích chuỗi tĩnh:

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

Điều này thêm một chức năng mở rộng để bạn có thể chỉ cần:

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

5
Tôi phát hiện ra rằng luồng trả về bị đóng (gây ra ngoại lệ bán ngẫu nhiên) khi trình thu gom rác dọn sạch StreamWriter. Cách khắc phục là sử dụng một hàm tạo khác - một hàm cho phép tôi chỉ định leftOpen .
Bevan

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

24

Sử dụng MemoryStreamlớp, gọi Encoding.GetBytesđể biến chuỗi của bạn thành một mảng byte trước tiên.

Bạn có cần một TextReaderluồng không? Nếu vậy, bạn có thể cung cấp StringReadertrực tiếp, và bỏ qua MemoryStreamEncodingcác bước.


23

Tôi đã sử dụng kết hợp các câu trả lời như thế này:

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

Và sau đó tôi sử dụng nó như thế này:

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

Thomas, tại sao lại bỏ phiếu? enc = enc ?? Encoding.UTF8 cho phép tôi hỏi cụ thể luồng với mã hóa cụ thể hoặc mặc định là UTF8 và vì trong .net (theo tôi sử dụng nó .net 4.0), bạn không thể cung cấp loại tham chiếu ngoài chuỗi giá trị mặc định trong hàm chữ ký dòng này là cần thiết, điều đó có ý nghĩa?
Robocide

đề cập rằng bạn cần đặt điều này trong một lớp riêng biệt (lớp tĩnh không chung chung?) cũng hữu ích và giảm số phiếu giảm.
Ali

13

Chúng tôi sử dụng các phương pháp mở rộng được liệt kê dưới đây. Tôi nghĩ bạn nên làm cho nhà phát triển đưa ra quyết định về mã hóa, vì vậy có ít phép thuật liên quan hơn.

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
Tôi muốn thực hiện các phương pháp đầu tiên như return ToStream(s, Encoding.UTF8);. Trong triển khai hiện tại ( return s.ToStream(Encoding.UTF8);, nhà phát triển buộc phải suy nghĩ nhiều hơn để nắm bắt mã và có vẻ như trường hợp s == nullkhông được xử lý và ném NullReferenceException.
Palec

10

Ở đây bạn đi:

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

1
Vị trí cần phải được thiết lập lại sau khi viết. Tốt hơn là sử dụng hàm tạo, như trong câu trả lời của joelnet.
Jim Balter

10

Phiên bản hiện đại hóa và sửa đổi một chút của các phương thức mở rộng cho 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));

Sửa đổi theo đề xuất trong bình luận của @ Palec về câu trả lời của @Shaun Bowe.



4

Nếu bạn cần thay đổi mã hóa, tôi bỏ phiếu cho giải pháp của @ShaunBowe . Nhưng mỗi câu trả lời ở đây sao chép toàn bộ chuỗi trong bộ nhớ ít nhất một lần. Các câu trả lời với ToCharArray+ BlockCopycombo làm điều đó hai lần.

Nếu vấn đề ở đây là một Streamtrình bao bọc đơn giản cho chuỗi UTF-16 thô. Nếu được sử dụng với một StreamReaderlựa chọn Encoding.Unicodecho nó:

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

đây là một giải pháp hoàn chỉnh hơn với kiểm tra ràng buộc cần thiết (có nguồn gốc từ MemoryStreamvì vậy nó có ToArrayWriteTophương pháp cũng).


-2

Một sự kết hợp tốt của các phần mở rộng Chuỗi:

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;
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.