2013-07-23 20 views
104

Voglio serializzare oggetti su stringhe e tornare indietro.Conversione di stream in stringa e ritorno ... cosa ci manca?

Utilizziamo protobuf-net per trasformare un oggetto in uno stream e tornare indietro, con successo.

Tuttavia, Streaming per stringa e ritorno ... non ha avuto molto successo. Dopo aver attraversato StreamToString e StringToStream, il nuovo Stream non è deserializzato da protobuf-net; solleva un'eccezione Arithmetic Operation resulted in an Overflow. Se deserializziamo il flusso originale, funziona.

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 utilizzando 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); 
+0

Modificato per chiarimenti, ty. – flipuhdelphia

+0

non dovrebbe Stream essere MemoryStrea? – Ehsan

risposta

46

Questo è così comune ma così profondamente sbagliato. I dati di Protobuf non sono dati di stringa. Certamente non è ASCII. Stai utilizzando la codifica all'indietro. Un trasferimento di codifica del testo:

  • una stringa arbitraria di byte formattato
  • byte formattati alla stringa

originale Non è "formattato byte". Hai byte arbitrari. È necessario utilizzare qualcosa come una codifica base-n (comunemente: base-64). Questo trasferisce

  • byte arbitrari ad una stringa formattata
  • una stringa formattata per il byte originale

sguardo Convert.ToBase64String e Convertire. FromBase64String

+1

potresti usare un ['BinaryFormatter'] (http://msdn.microsoft.com/en-us/library/system.runtime.serialization.formatters.binary.binaryformatter (v = vs.110) .aspx), simile a questo [esempio strano] (http://stackoverflow.com/a/1133465/1037948)? – drzaus

+0

@drzaus hm ... forse no:> "Qualsiasi carattere surrogato non accoppiato viene perso nella serializzazione binaria" – drzaus

+15

Beh, questo è il primo @drzaus ... Mai visto nessuno parlare da solo su SO. –

121

ho appena testato questa 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, si potrebbe voler cercare l'inizio prima della prima lettura del testo: stream.Seek(0, SeekOrigin.Begin);

5

Quando si prova prova con flusso UTF8 Encode come qui di seguito

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

Prova questo.

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

In caso d'uso in cui si desidera serializzare/deserializzare pocos, biblioteca JSON di Newtonsoft è veramente buono. Lo uso per mantenere POCO in SQL Server come stringhe JSON in un campo nvarchar. L'avvertenza è che dal momento che non è vero de/serializzazione, non preserverà i membri privati ​​/ protetti e la gerarchia delle classi.

4

un UTF8 MemoryStream alla conversione String:

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

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 https://msdn.microsoft.com/en-us/library/system.io.memorystream.getbuffer.aspx?ranMID=24542&ranEAID=TnL5HPStwNw&ranSiteID=TnL5HPStwNw-fWxoHnp968IcSsgF87gs3g&tduid=(af5d64506773ff2a87e3cde51de009eb)(256380)(2459594)(TnL5HPStwNw- fWxoHnp968IcSsgF87gs3g)() – Mortennobel

0

Ho scritto un metodo utile per chiamare qualsiasi azione che prende un StreamWriter e scrivere fuori per una stringa invece.Il metodo è come questo;

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 lo si può usare così;

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ù facile con un StringBuilder, il punto è che si può chiamare qualsiasi metodo che prende un StreamWriter.