Como faço para gerar um fluxo a partir de uma string?


Respostas:


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

Não se esqueça de usar o Using:

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

Sobre o StreamWriternão ser descartado. StreamWriteré apenas um invólucro em torno do fluxo base e não usa nenhum recurso que precise ser descartado. O Disposemétodo fechará o subjacente Streamque StreamWriterestá sendo gravado. Nesse caso, é o MemoryStreamque queremos retornar.

Agora, no .NET 4.5, existe uma sobrecarga StreamWriterque mantém o fluxo subjacente aberto depois que o gravador é descartado, mas esse código faz a mesma coisa e funciona com outras versões do .NET também.

Consulte Existe alguma maneira de fechar um StreamWriter sem fechar seu BaseStream?


134
Um conceito de ponto importante a ser destacado é que um fluxo é composto de bytes, enquanto uma sequência é composta de caracteres. É crucial entender que a conversão de um caractere em um ou mais bytes (ou em um fluxo como neste caso) sempre usa (ou assume) uma codificação específica. Essa resposta, embora correta em alguns casos, usa a codificação padrão e pode não ser adequada em geral. A passagem explícita de uma codificação para o construtor StreamWriter tornaria mais evidente que o autor precisa considerar as implicações da codificação.
drwatsoncode

6
Você diz "Não se esqueça de usar o Using" para usar o fluxo, mas no seu GenerateStreamFromStringmétodo você não está usando o Using with the StreamWriter. Existe uma razão para isso?
2020 Ben

12
@ Ben Sim. Se você descartar o StreamWriter, o fluxo subjacente também será fechado. Nós não queremos isso. O único motivo pelo qual o Writer é descartável é limpar o fluxo, portanto é seguro ignorar.
Cameron MacFarland

2
Também deve ser observado que toda a cadeia é copiada para uma memória que pode ser importante para cadeias grandes, porque agora temos uma cópia extra na memória.
UGEEN

1
@ahong Na verdade não. StreamWriterprovavelmente está fazendo o que você disse internamente de qualquer maneira. A vantagem é o encapsulamento e o código mais simples, mas com o custo de abstrair coisas como a codificação. Depende do que você está tentando alcançar.
Cameron MacFarland

724

Outra solução:

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

31
Caso alguém use isso com uma desserialização de string XML, tive que mudar o UTF8 para Unicode para que funcione sem um sinalizador. Ótimo post!!!
Gaspa79

2
Eu gosto mais deste (com o ajuste de Rhyous e o açúcar extra trivial para uso como método de extensão) do que a resposta aceita; mais flexível, menos LOC e menos objetos envolvidos (sem necessidade explícita de um StreamWriter)
Keiths

2
new MemoryStream(Encoding.UTF8.GetBytes("\ufeff" + (value ?? ""))se você precisar incluir a lista técnica no início do fluxo
robert4

5
Essa sintaxe é muito compacta, mas causará muitas alocações de byte [], portanto, tenha cuidado no código de alto desempenho.
michael.aird

1
Essa solução ainda deixou a oportunidade de tornar o fluxo somente leitura. new MemoryStream( value, false ). Você não pode criar um fluxo somente leitura se precisar gravá-lo com um gravador de fluxo.
codekandis

106

Adicione isso a uma classe de utilitário de cadeia estática:

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

Isso adiciona uma função de extensão para que você possa simplesmente:

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

5
Descobri que o fluxo retornado é fechado (causando exceções semi-aleatórias) quando o coletor de lixo limpa o StreamWriter. A correção foi usar um construtor diferente - um que me permitisse especificar leaveOpen .
Bevan

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

24

Use a MemoryStreamclasse chamando Encoding.GetBytespara transformar sua string em uma matriz de bytes primeiro.

Você precisa posteriormente de um TextReaderstream? Nesse caso, você pode fornecer um StringReaderdiretamente e ignorar as etapas MemoryStreame Encoding.


23

Eu usei uma mistura de respostas como esta:

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

E então eu uso assim:

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

Thomas, por que votar? enc = enc ?? O Encoding.UTF8 permite que eu pergunte especificamente ao fluxo com codificação específica, ou um padrão de UTF8, e porque em .net (até onde eu o uso .net 4.0) você não pode atribuir a um tipo de referência diferente de string um valor padrão na função assinatura esta linha é necessária, isso faz sentido?
Robocide 03/02

mencionar que você precisa colocar isso em uma classe separada (classe estática não genérica?) também é útil e reduz os votos negativos.
Ali

13

Usamos os métodos de extensão listados abaixo. Eu acho que você deve fazer o desenvolvedor tomar uma decisão sobre a codificação, para que haja menos mágica envolvida.

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
Eu preferiria implementar o primeiro método como return ToStream(s, Encoding.UTF8);. Na implementação atual ( return s.ToStream(Encoding.UTF8);, o desenvolvedor é obrigado a pensar mais difícil de entender o código e parece que o caso de s == nullse não tratada e joga NullReferenceException.
Palec

10

Aqui está:

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

1
A posição precisa ser redefinida após a gravação. Melhor usar o construtor, como na resposta do joelnet.
Jim Balter

10

Versão modernizada e ligeiramente modificada dos métodos de extensão para 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ção sugerida no comentário de @ Palec da resposta de @ Shaun Bowe.



4

Se você precisar alterar a codificação, voto na solução de @ShaunBowe . Mas todas as respostas aqui copiam toda a cadeia de caracteres da memória pelo menos uma vez. As respostas com ToCharArray+BlockCopy combo fazem isso duas vezes.

Se isso importa aqui, é um Streamwrapper simples para a sequência UTF-16 bruta. Se usado com uma StreamReaderseleção Encoding.Unicodepara ele:

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

E aqui está uma solução mais completa, com as verificações vinculadas necessárias (derivadas de MemoryStreamoutras formas ToArraye WriteTométodos).


-2

Uma boa combinação de extensões 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;
}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.