Conversione da Stream a String e viceversa ... cosa ci manca?


162

Voglio serializzare gli oggetti su stringhe e viceversa.

Usiamo protobuf-net per trasformare un oggetto in un flusso e viceversa, con successo.

Tuttavia, esegui lo streaming su stringa e ritorno ... non è così efficace. Dopo aver attraversato StreamToStringe StringToStream, il nuovo Streamnon è deserializzato da protobuf-net; solleva Arithmetic Operation resulted in an Overflowun'eccezione. Se deserializziamo lo stream originale, funziona.

I nostri metodi:

public static string StreamToString(Stream stream)
{
    stream.Position = 0;
    using (StreamReader reader = new StreamReader(stream, Encoding.UTF8))
    {
        return reader.ReadToEnd();
    }
}

public static Stream StringToStream(string src)
{
    byte[] byteArray = Encoding.UTF8.GetBytes(src);
    return new MemoryStream(byteArray);
}

Il nostro codice di esempio utilizza questi due:

MemoryStream stream = new MemoryStream();
Serializer.Serialize<SuperExample>(stream, test);
stream.Position = 0;
string strout = StreamToString(stream);
MemoryStream result = (MemoryStream)StringToStream(strout);
var other = Serializer.Deserialize<SuperExample>(result);

1
Stream non dovrebbe essere MemoryStrea?
Ehsan,

Risposte:


60

Questo è così comune ma così profondamente sbagliato. I dati di Protobuf non sono dati di stringa. Certamente non è ASCII. Stai utilizzando la codifica al contrario . Una codifica di testo trasferisce:

  • una stringa arbitraria in byte formattati
  • byte formattati nella stringa originale

Non hai "byte formattati". Hai byte arbitrari . Devi usare qualcosa come una codifica base-n (comunemente: base-64). Questo trasferisce

  • byte arbitrari a una stringa formattata
  • una stringa formattata ai byte originali

guarda Convert.ToBase64String e Convert. FromBase64String


1
potresti usare un BinaryFormatter, simile a questo strano esempio ?
drzaus,

@drzaus hm ... forse no:> "Qualsiasi personaggio surrogato
spaiato

211

Ho appena provato questo e funziona bene.

string test = "Testing 1-2-3";

// convert string to stream
byte[] byteArray = Encoding.ASCII.GetBytes(test);
MemoryStream stream = new MemoryStream(byteArray);

// convert stream to string
StreamReader reader = new StreamReader(stream);
string text = reader.ReadToEnd();

Se streamè già stato scritto, potresti cercare all'inizio prima prima di leggere il testo:stream.Seek(0, SeekOrigin.Begin);


E non dimenticare un blocco che utilizza intorno StreamReader reader = new StreamReader (stream);
PRMan,

7

una conversione da MemoryStream a String UTF8:

var res = Encoding.UTF8.GetString(stream.GetBuffer(), 0 , (int)stream.Length)

2
Utilizzare invece ToArray (). Il buffer può essere più grande della dimensione dei dati utilizzati. ToArray () restituisce una copia dei dati con la dimensione corretta. var array = stream.ToArray(); var str = Encoding.UTF8.GetString(array, 0, array.Length);. Vedi anche msdn.microsoft.com/en-us/library/...
Mortennobel

1
@Mortennobel ToArray()alloca un nuovo array in memoria e copia i dati dal buffer, il che può avere serie implicazioni se si ha a che fare con molti dati.
Levi Botelho,

Nota l'uso di stream.Length, piuttosto che stream.GetBuffer (). Lunghezza. E Levi ha notato correttamente il motivo per cui non è stato utilizzato ToArray ().
Wolfgang Grinfeld,

5

Durante il test, prova con il UTF8flusso Encode come di seguito

var stream = new MemoryStream();
var streamWriter = new StreamWriter(stream, System.Text.Encoding.UTF8);
Serializer.Serialize<SuperExample>(streamWriter, test);

5

Prova questo.

string output1 = Encoding.ASCII.GetString(byteArray, 0, byteArray.Length)

2

Ho scritto un metodo utile per chiamare qualsiasi azione che prende un StreamWritere scriverlo invece in una stringa. Il metodo è così;

static void SendStreamToString(Action<StreamWriter> action, out string destination)
{
    using (var stream = new MemoryStream())
    using (var writer = new StreamWriter(stream, Encoding.Unicode))
    {
        action(writer);
        writer.Flush();
        stream.Position = 0;
        destination = Encoding.Unicode.GetString(stream.GetBuffer(), 0, (int)stream.Length);
    }
}

E puoi usarlo in questo modo;

string myString;

SendStreamToString(writer =>
{
    var ints = new List<int> {1, 2, 3};
    writer.WriteLine("My ints");
    foreach (var integer in ints)
    {
        writer.WriteLine(integer);
    }
}, out myString);

So che questo può essere fatto molto più facilmente con un StringBuilder, il punto è che puoi chiamare qualsiasi metodo che richiede un StreamWriter.


1

Voglio serializzare gli oggetti su stringhe e viceversa.

Diverso dalle altre risposte, ma il modo più semplice per farlo esattamente per la maggior parte dei tipi di oggetti è XmlSerializer:

        Subject subject = new Subject();
        XmlSerializer serializer = new XmlSerializer(typeof(Subject));
        using (Stream stream = new MemoryStream())
        {
            serializer.Serialize(stream, subject);
            // do something with stream
            Subject subject2 = (Subject)serializer.Deserialize(stream);
            // do something with subject2
        }

Tutte le proprietà pubbliche dei tipi supportati verranno serializzate. Anche alcune strutture di raccolta sono supportate e scorrono tunnel verso le proprietà degli oggetti secondari. Puoi controllare come funziona la serializzazione con gli attributi delle tue proprietà.

Questo non funziona con tutti i tipi di oggetto, alcuni tipi di dati non sono supportati per la serializzazione, ma nel complesso è piuttosto potente e non devi preoccuparti della codifica.


0

Nel caso in cui si desidera serializzare / deserializzare i POCO, la libreria JSON di Newtonsoft è davvero ottima. Lo uso per mantenere POCO all'interno di SQL Server come stringhe JSON in un campo nvarchar. Un avvertimento è che, poiché non è una vera de / serializzazione, non conserverà i membri privati ​​/ protetti e la gerarchia di classi.

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.