ฉันจะสร้างสตรีมจากสตริงได้อย่างไร


759

ฉันต้องเขียนการทดสอบหน่วยสำหรับวิธีที่ใช้กระแสซึ่งมาจากไฟล์ข้อความ ฉันต้องการทำสิ่งนี้:

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

สำหรับหน่วยความจำวิธีการแก้ปัญหาการประหยัดดูStringReaderStreamในstackoverflow.com/a/55170901/254109
xmedeko

คำตอบ:


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

อย่าลืมที่จะใช้การใช้:

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

เกี่ยวกับการStreamWriterไม่ถูกกำจัด StreamWriterเป็นเพียงเสื้อคลุมรอบสตรีมพื้นฐานและไม่ใช้ทรัพยากรใด ๆ ที่จำเป็นต้องกำจัด Disposeวิธีการจะปิดพื้นฐานStreamที่StreamWriterจะเขียนถึง ในกรณีนี้คือสิ่งที่MemoryStreamเราต้องการคืน

ใน. NET 4.5 ขณะนี้มีการโอเวอร์โหลดสำหรับStreamWriterการเปิดกระแสข้อมูลพื้นฐานหลังจากที่ผู้เขียนถูกกำจัด แต่รหัสนี้ทำสิ่งเดียวกันและทำงานร่วมกับ. NET รุ่นอื่นด้วย

ดูมีวิธีปิด StreamWriter โดยไม่ปิด BaseStream หรือไม่


134
แนวคิดของจุดสำคัญที่จะชี้ให้เห็นก็คือสตรีมประกอบด้วยไบต์ในขณะที่สตริงประกอบด้วยอักขระ มันเป็นสิ่งสำคัญที่จะต้องเข้าใจว่าการแปลงอักขระเป็นหนึ่งไบต์ขึ้นไป (หรือสตรีมดังในกรณีนี้) จะใช้การเข้ารหัสแบบใดแบบหนึ่งเสมอ คำตอบนี้ในขณะที่ถูกต้องในบางกรณีใช้การเข้ารหัสเริ่มต้นและอาจไม่เหมาะสมโดยทั่วไป การส่งผ่านการเข้ารหัสไปยังตัวสร้าง StreamWriter อย่างชัดเจนจะทำให้ชัดเจนยิ่งขึ้นว่าผู้เขียนต้องพิจารณาความหมายของการเข้ารหัส
drwatsoncode

6
คุณพูดว่า "อย่าลืมใช้การใช้" เพื่อใช้กระแสข้อมูล แต่ในGenerateStreamFromStringวิธีการของคุณคุณไม่ได้ใช้การใช้กับ StreamWriter มีเหตุผลสำหรับสิ่งนี้หรือไม่?
Ben

12
@Ben ใช่ หากคุณทิ้ง StreamWriter สตรีมพื้นฐานจะถูกปิดเช่นกัน เราไม่ต้องการสิ่งนั้น เหตุผลเดียวที่นักเขียนใช้แล้วทิ้งคือการล้างสตรีมดังนั้นจึงปลอดภัยที่จะไม่สนใจ
Cameron MacFarland

2
ควรสังเกตว่าสตริงทั้งหมดจะถูกคัดลอกไปยังหน่วยความจำซึ่งอาจมีความสำคัญสำหรับสตริงขนาดใหญ่เพราะตอนนี้เรามีสำเนาเพิ่มเติมหนึ่งในหน่วยความจำ
UGEEN

1
@ ahong ไม่จริง StreamWriterอาจทำในสิ่งที่คุณพูดภายใน ข้อดีคือ encapsulation และรหัสที่ง่ายกว่า แต่มีค่าใช้จ่ายในการสรุปสิ่งต่าง ๆ เช่นการเข้ารหัสออกไป ขึ้นอยู่กับสิ่งที่คุณพยายามทำ
คาเมรอน MacFarland

724

ทางออกอื่น:

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

31
ในกรณีที่มีคนใช้สิ่งนี้กับการดีซีเรียลไลซ์สตริงของ XML ฉันต้องสลับ UTF8 เป็น Unicode เพื่อให้มันทำงานได้โดยไม่ต้องตั้งค่าสถานะ โพสต์ยอดเยี่ยม !!!
Gaspa79

2
ฉันชอบอันนี้ (ด้วยการปรับแต่งของ Rhyous และน้ำตาลพิเศษเล็กน้อยเพื่อใช้เป็นวิธีการขยาย) ดีกว่าคำตอบที่ยอมรับ มีความยืดหยุ่นมากขึ้น LOC ที่น้อยลงและวัตถุที่เกี่ยวข้องน้อยลง (ไม่จำเป็นต้องใช้ StreamWriter อย่างชัดเจน)
KeithS

2
new MemoryStream(Encoding.UTF8.GetBytes("\ufeff" + (value ?? ""))ถ้าคุณต้องการให้ BOM รวมไว้ที่จุดเริ่มต้นของสตรีม
robert4

5
นี่เป็นไวยากรณ์ขนาดเล็กมาก แต่จะทำให้มีการจัดสรรจำนวนมากของไบต์ [] ดังนั้นจงระวังในรหัสประสิทธิภาพสูง
michael.aird

1
โซลูชันนี้ยังคงโอกาสในการสร้างกระแสข้อมูลแบบอ่านอย่างเดียว new MemoryStream( value, false ). คุณไม่สามารถสร้างสตรีมแบบอ่านอย่างเดียวได้หากคุณต้องเขียนด้วยสตรีมตัวเขียน
codekandis

106

เพิ่มสิ่งนี้ไปยังคลาสยูทิลิตี้สตริงคงที่:

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

นี่เป็นการเพิ่มฟังก์ชั่นเสริมเพื่อให้คุณสามารถ:

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

5
ผมค้นพบว่ากระแสกลับได้รับปิด (ก่อให้เกิดข้อยกเว้นกึ่งสุ่ม) StreamWriterเมื่อเก็บขยะทำความสะอาดขึ้น การแก้ไขคือการใช้ตัวสร้างที่แตกต่างกัน - หนึ่งที่อนุญาตให้ฉันไประบุleaveOpen
Bevan


24

ใช้MemoryStreamคลาสโทรEncoding.GetBytesเพื่อเปลี่ยนสตริงของคุณให้เป็นอาร์เรย์ไบต์ก่อน

คุณจำเป็นต้องมีTextReaderสตรีมในภายหลังหรือไม่? ถ้าเป็นเช่นนั้นคุณสามารถจัดหาStringReaderโดยตรงและข้ามMemoryStreamและEncodingขั้นตอน


23

ฉันใช้คำตอบแบบผสม:

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

แล้วฉันจะใช้มันเช่นนี้

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

โทมัสลงคะแนนทำไม? enc = enc ?? Encoding.UTF8 อนุญาตให้ฉันถามกระแสโดยเฉพาะด้วยการเข้ารหัสเฉพาะหรือค่าเริ่มต้นของ UTF8 และเพราะใน. net (เท่าที่ฉันใช้. net 4.0) คุณไม่สามารถให้ประเภทอ้างอิงอื่นนอกเหนือจากสตริงเป็นค่าเริ่มต้นในฟังก์ชั่น จำเป็นต้องมีลายเซ็นของสายนี้
Robocide

การกล่าวถึงว่าคุณต้องใส่สิ่งนี้ในคลาสที่แยกต่างหาก (ไม่ใช่คลาสแบบสแตติกทั่วไป?) ก็มีประโยชน์เช่นกันและลดคะแนนลง
อาลี

13

เราใช้วิธีการขยายที่แสดงด้านล่าง ฉันคิดว่าคุณควรให้นักพัฒนาตัดสินใจเกี่ยวกับการเข้ารหัสดังนั้นจึงมีเวทมนตร์น้อยลง

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
return ToStream(s, Encoding.UTF8);ฉันชอบที่จะใช้วิธีแรกเป็น ในการดำเนินการปัจจุบัน ( return s.ToStream(Encoding.UTF8);นักพัฒนาที่ถูกบังคับให้ต้องคิดยากที่จะเข้าใจรหัสและดูเหมือนว่ากรณีที่s == nullเป็นไม่สามารถจัดการได้และพ่นNullReferenceException.
Palec

10

ไปเลย:

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

1
ตำแหน่งจะต้องรีเซ็ตหลังจากเขียน ดีกว่าที่จะใช้ตัวสร้างเช่นเดียวกับในคำตอบของ joelnet
Jim Balter

10

วิธีการขยายรุ่นที่ทันสมัยและแก้ไขเล็กน้อยสำหรับ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));

การแก้ไขตามที่แนะนำไว้ในความคิดเห็นของ @ Palec ของคำตอบ @Shaun Bowe


8

ฉันคิดว่าคุณสามารถได้รับประโยชน์จากการใช้MemoryStream คุณสามารถกรอกด้วยสตริงไบต์ที่คุณได้รับโดยใช้GetBytesวิธีการระดับการเข้ารหัส


4

หากคุณต้องการเปลี่ยนการเข้ารหัสฉันลงคะแนนให้โซลูชันของ@ShaunBowe แต่ทุกคำตอบที่นี่คัดลอกสตริงทั้งหมดในหน่วยความจำอย่างน้อยหนึ่งครั้ง คำตอบด้วยToCharArray+ BlockCopyคอมโบทำได้สองครั้ง

หากสิ่งที่สำคัญตรงนี้เป็นStreamเครื่องห่อหุ้มอย่างง่ายสำหรับสตริง raw UTF-16 หากใช้กับตัวStreamReaderเลือก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; // ;)     
}

และนี่คือโซลูชันที่สมบูรณ์ยิ่งขึ้นด้วยการตรวจสอบขอบเขตที่จำเป็น (ซึ่งได้มาจากMemoryStreamมันToArrayและWriteToวิธีการเช่นกัน)


-2

การรวมกันที่ดีของส่วนขยายของสตริง:

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;
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.