Conversion de flux en chaîne et inversement… que nous manque-t-il?

162

Je veux sérialiser des objets en chaînes et inversement.

Nous utilisons protobuf-net pour transformer un objet en Stream et inversement, avec succès.

Cependant, Stream to string et retour ... pas si réussi. Après avoir traversé StreamToStringet StringToStream, le nouveau Streamn'est pas désérialisé par protobuf-net; il soulève une Arithmetic Operation resulted in an Overflowexception. Si nous désérialisons le flux d'origine, cela fonctionne.

Nos méthodes:

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

Notre exemple de code utilisant ces deux:

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
la source
1
Stream ne devrait-il pas être MemoryStrea?
Ehsan

Réponses:

60

C'est si courant mais si profondément faux. Les données Protobuf ne sont pas des données de type chaîne. Ce n'est certainement pas ASCII. Vous utilisez l'encodage à l' envers . Un encodage de texte transfère:

  • une chaîne arbitraire en octets formatés
  • octets formatés à la chaîne d'origine

Vous n'avez pas d '"octets formatés". Vous avez des octets arbitraires . Vous devez utiliser quelque chose comme un encodage base-n (généralement: base-64). Ce transfert

  • octets arbitraires en chaîne formatée
  • une chaîne formatée aux octets d'origine

regardez Convert.ToBase64String et Convertir. FromBase64String

Marc Gravell
la source
1
pourriez-vous utiliser un BinaryFormatter, similaire à cet exemple étrange ?
drzaus
@drzaus hm ... peut-être pas:> "Tous les caractères de substitution non appariés sont perdus dans la sérialisation binaire"
drzaus
211

Je viens de tester cela et fonctionne très 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 streama déjà été écrit, vous voudrez peut-être chercher au début avant de commencer avant de lire le texte:stream.Seek(0, SeekOrigin.Begin);

Ehsan
la source
Et n'oubliez pas un bloc d'utilisation autour de StreamReader reader = new StreamReader (stream);
PRMan
7

une conversion UTF8 MemoryStream en chaîne:

var res = Encoding.UTF8.GetString(stream.GetBuffer(), 0 , (int)stream.Length)
Wolfgang Grinfeld
la source
2
Utilisez plutôt ToArray (). Le tampon peut être plus grand que la taille des données utilisées. ToArray () renvoie une copie des données avec la taille correcte. var array = stream.ToArray(); var str = Encoding.UTF8.GetString(array, 0, array.Length);. Voir aussi msdn.microsoft.com/en-us/library/…
Mortennobel
1
@Mortennobel ToArray()alloue un nouveau tableau en mémoire et copie les données du tampon, ce qui peut avoir de graves implications si vous traitez beaucoup de données.
Levi Botelho
Notez l'utilisation de stream.Length, plutôt que stream.GetBuffer (). Length. Et Levi a correctement noté la raison de ne pas utiliser ToArray ().
Wolfgang Grinfeld
5

Lorsque vous testez, essayez avec le UTF8flux Encode comme ci-dessous

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

Essaye ça.

string output1 = Encoding.ASCII.GetString(byteArray, 0, byteArray.Length)
user3588327
la source
2

J'ai écrit une méthode utile pour appeler toute action qui prend un StreamWriteret l'écrire dans une chaîne à la place. La méthode est comme ça;

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

Et vous pouvez l'utiliser comme ça;

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

Je sais que cela peut être fait beaucoup plus facilement avec a StringBuilder, le fait est que vous pouvez appeler n'importe quelle méthode qui prend a StreamWriter.

Steztric
la source
1

Je veux sérialiser des objets en chaînes et inversement.

Différent des autres réponses, mais le moyen le plus simple de faire exactement cela pour la plupart des types d'objets est 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
        }

Toutes vos propriétés publiques des types pris en charge seront sérialisées. Même certaines structures de collection sont prises en charge et aboutiront aux propriétés des sous-objets. Vous pouvez contrôler le fonctionnement de la sérialisation avec les attributs de vos propriétés.

Cela ne fonctionne pas avec tous les types d'objets, certains types de données ne sont pas pris en charge pour la sérialisation, mais dans l'ensemble, c'est assez puissant et vous n'avez pas à vous soucier de l'encodage.

Denise Skidmore
la source
0

Dans le cas d'utilisation où vous souhaitez sérialiser / désérialiser les POCO, la bibliothèque JSON de Newtonsoft est vraiment bonne. Je l'utilise pour conserver les POCO dans SQL Server en tant que chaînes JSON dans un champ nvarchar. La mise en garde est que, comme ce n'est pas une véritable dés / sérialisation, il ne conservera pas les membres privés / protégés et la hiérarchie des classes.

MD Luffy
la source