Convirtiendo Stream a String y viceversa ... ¿qué nos estamos perdiendo?

162

Quiero serializar objetos en cadenas y viceversa.

Utilizamos protobuf-net para convertir un objeto en Stream y viceversa, con éxito.

Sin embargo, Transmitir a cadena y viceversa ... no tan exitoso. Después de pasar StreamToStringy StringToStream, lo nuevo Streamno es deserializado por protobuf-net; plantea una Arithmetic Operation resulted in an Overflowexcepción. Si deserializamos la secuencia original, funciona.

Nuestros métodos:

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

Nuestro código de ejemplo usando estos dos:

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);
flipuhdelphia
fuente
1
¿no debería Stream ser MemoryStrea?
Ehsan

Respuestas:

60

Esto es tan común pero tan profundamente equivocado. Los datos de Protobuf no son datos de cadena. Ciertamente no es ASCII. Estás utilizando la codificación al revés . Una codificación de texto transfiere:

  • una cadena arbitraria a bytes formateados
  • bytes formateados a la cadena original

No tiene "bytes formateados". Tienes bytes arbitrarios . Necesita usar algo como una codificación base-n (comúnmente: base-64). Esto transfiere

  • bytes arbitrarios a una cadena formateada
  • una cadena formateada a los bytes originales

mira Convert.ToBase64String y Convert. FromBase64String

Marc Gravell
fuente
1
¿podrías usar un ejemploBinaryFormatter similar a este extraño ?
drzaus
@drzaus hm ... tal vez no:> "Cualquier personaje sustituto no apareado se pierde en la serialización binaria"
drzaus
210

Acabo de probar esto y funciona bien.

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();

Si streamya se ha escrito, es posible que desee buscar el principio antes del primero antes de leer el texto:stream.Seek(0, SeekOrigin.Begin);

Ehsan
fuente
Y no olvide un bloque de uso alrededor de StreamReader reader = new StreamReader (stream);
PRMan
7

una conversión de UTF8 MemoryStream a String:

var res = Encoding.UTF8.GetString(stream.GetBuffer(), 0 , (int)stream.Length)
Wolfgang Grinfeld
fuente
2
Utilice ToArray () en su lugar. El búfer puede ser mayor que el tamaño de los datos utilizados. ToArray () devuelve una copia de los datos con el tamaño correcto. var array = stream.ToArray(); var str = Encoding.UTF8.GetString(array, 0, array.Length);. Ver también msdn.microsoft.com/en-us/library/…
Mortennobel
1
@Mortennobel ToArray()asigna una nueva matriz en la memoria y copia los datos del búfer, lo que puede tener serias implicaciones si se trata de una gran cantidad de datos.
Levi Botelho
Tenga en cuenta el uso de stream.Length, en lugar de stream.GetBuffer (). Length. Y Levi notó correctamente la razón por la que no usa ToArray ().
Wolfgang Grinfeld
5

Cuando realice la prueba, intente con la UTF8secuencia Encode como se muestra a continuación

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

Prueba esto.

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

Escribí un método útil para llamar a cualquier acción que tome una StreamWritery escribirla en una cadena. El método es así;

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

Y puedes usarlo así;

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

Sé que esto se puede hacer mucho más fácil con a StringBuilder, el punto es que puedes llamar a cualquier método que requiera a StreamWriter.

Steztric
fuente
1

Quiero serializar objetos en cadenas y viceversa.

Diferente de las otras respuestas, pero la forma más directa de hacer exactamente eso para la mayoría de los tipos de objetos es 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
        }

Todas sus propiedades públicas de los tipos admitidos serán serializadas. Incluso algunas estructuras de colección son compatibles, y se canalizarán a propiedades de subobjeto. Puede controlar cómo funciona la serialización con atributos en sus propiedades.

Esto no funciona con todos los tipos de objetos, algunos tipos de datos no son compatibles con la serialización, pero en general es bastante potente y no tiene que preocuparse por la codificación.

Denise Skidmore
fuente
0

En caso de que desee serializar / deserializar POCO, la biblioteca JSON de Newtonsoft es realmente buena. Lo uso para persistir POCO dentro de SQL Server como cadenas JSON en un campo nvarchar. La advertencia es que, dado que no es una deserialización / serialización verdadera, no conservará los miembros privados / protegidos ni la jerarquía de clases.

MD Luffy
fuente