Come posso generare uno stream da una stringa?


Risposte:


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

Non dimenticare di usare usando:

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

A proposito del StreamWriternon essere smaltito. StreamWriterè solo un wrapper attorno al flusso di base e non utilizza alcuna risorsa che deve essere eliminata. Il Disposemetodo chiuderà il sottostante su Streamcui StreamWritersta scrivendo. In questo caso è quello MemoryStreamche vogliamo tornare.

In .NET 4.5 ora esiste un sovraccarico StreamWriterche mantiene aperto il flusso sottostante dopo l'eliminazione del writer, ma questo codice fa la stessa cosa e funziona anche con altre versioni di .NET.

Vedi Esiste un modo per chiudere uno StreamWriter senza chiudere BaseStream?


134
Un importante concetto di punto da sottolineare è che uno stream è composto da byte, mentre una stringa è composta da caratteri. È fondamentale capire che la conversione di un carattere in uno o più byte (o in un flusso come in questo caso) utilizza sempre (o assume) una codifica particolare. Questa risposta, sebbene corretta in alcuni casi, utilizza la codifica predefinita e potrebbe non essere adatta in generale. Il passaggio esplicito di una codifica al costruttore di StreamWriter renderebbe più evidente che l'autore deve considerare le implicazioni della codifica.
drwatscodice

6
Dici "Non dimenticare di usare l'Uso" per usare lo stream, ma nel tuo GenerateStreamFromStringmetodo non stai usando l'Uso con StreamWriter. C'è una ragione per questo?
Ben

12
@Ben Sì. Se si elimina StreamWriter, anche lo stream sottostante verrà chiuso. Non lo vogliamo. L'unico motivo per cui Writer è usa e getta è ripulire il flusso, quindi è sicuro ignorarlo.
Cameron MacFarland

2
Va anche notato che l'intera stringa viene copiata in una memoria che può essere importante per stringhe di grandi dimensioni perché ora abbiamo una copia aggiuntiva in memoria.
UGEEN,

1
@ahong Non proprio. StreamWriterprobabilmente sta facendo quello che hai detto internamente. Il vantaggio è l'incapsulamento e il codice più semplice, ma a costo di astrarre cose come la codifica. Dipende da cosa stai cercando di ottenere.
Cameron MacFarland il

724

Un'altra soluzione:

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

31
Nel caso in cui qualcuno lo usi con una deserializzazione di stringhe XML, ho dovuto cambiare UTF8 in Unicode per farlo funzionare senza un flag. Ottimo post !!!
Gaspa79,

2
Mi piace questo (con il tweak di Rhyous e il banale zucchero extra da usare come metodo di estensione) meglio della risposta accettata; più flessibile, meno LOC e meno oggetti coinvolti (nessuna necessità esplicita di uno StreamWriter)
KeithS,

2
new MemoryStream(Encoding.UTF8.GetBytes("\ufeff" + (value ?? ""))se devi includere la distinta base all'inizio dello stream
robert4

5
Questa è una sintassi molto compatta ma causerà molte allocazioni di byte [], quindi fai attenzione nel codice ad alte prestazioni.
michael.aird,

1
Questa soluzione lasciava ancora l'opportunità di rendere il flusso in sola lettura. new MemoryStream( value, false ). Non è possibile creare uno stream in sola lettura se è necessario scriverlo con uno stream writer.
codekandis,

106

Aggiungi questo a una classe di utilità stringa statica:

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

Questo aggiunge una funzione di estensione in modo da poter semplicemente:

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

5
Ho scoperto che il flusso restituito viene chiuso (causando eccezioni semi-casuali) quando il garbage collector pulisce il file StreamWriter. La correzione consisteva nell'usare un costruttore diverso, uno che mi permettesse di specificare leaveOpen .
Bevan,

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

24

Utilizzare la MemoryStreamclasse, chiamando Encoding.GetBytesper trasformare prima la stringa in una matrice di byte.

Successivamente hai bisogno di uno TextReadersul flusso? In tal caso, è possibile fornire StringReaderdirettamente un e ignorare i passaggi MemoryStreame Encoding.


23

Ho usato un mix di risposte come questa:

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

E poi lo uso così:

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

Thomas, perché votare male? enc = enc ?? Encoding.UTF8 mi permette di chiedere in modo specifico stream con codifica specifica o un valore predefinito di UTF8 e poiché in .net (per quanto lo uso .net 4.0) non è possibile assegnare a un tipo di riferimento diverso dalla stringa un valore predefinito in funzione firma questa riga è necessaria, ha senso?
Robocide,

menzionare che è necessario inserirlo in una classe separata (classe statica non generica?) è utile e ridurre i voti negativi.
Ali,

13

Utilizziamo i metodi di estensione elencati di seguito. Penso che dovresti fare in modo che lo sviluppatore prenda una decisione sulla codifica, quindi c'è meno magia in gioco.

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
Preferirei implementare il primo metodo come return ToStream(s, Encoding.UTF8);. Nell'attuale implementazione ( return s.ToStream(Encoding.UTF8);, lo sviluppatore è costretto a pensare di più per afferrare il codice e sembra che il caso di s == nullsia gestito e gettato NullReferenceException.
Palec,

10

Ecco qui:

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

1
La posizione deve essere ripristinata dopo la scrittura. Meglio usare il costruttore, come nella risposta di Joelnet.
Jim Balter,

10

Versione modernizzata e leggermente modificata dei metodi di estensione per 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));

Modifica come suggerito nel commento di @ Palec della risposta di @Shaun Bowe.



4

Se hai bisogno di cambiare la codifica, voto per la soluzione di @ShaunBowe . Ma ogni risposta qui copia l'intera stringa in memoria almeno una volta. Le risposte con ToCharArray+ BlockCopycombo lo fanno due volte.

Se ciò che conta qui è un semplice Streamwrapper per la stringa grezza UTF-16. Se utilizzato con un StreamReaderselezionare Encoding.Unicodeper esso:

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

Ed ecco una soluzione più completa con i necessari controlli associati (derivati ​​da MemoryStreamcosì ha ToArraye anche WriteTometodi).


-2

Una buona combinazione di estensioni 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;
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.