Преобразование потока в строку и обратно ... что нам не хватает?


162

Я хочу сериализовать объекты в строки и обратно.

Мы используем protobuf-net, чтобы успешно превратить объект в поток и обратно.

Тем не менее, Stream для строки и обратно ... не так успешно. После прохождения StreamToStringи StringToStream, новое Streamне десериализовано protobuf-net; это вызывает Arithmetic Operation resulted in an Overflowисключение. Если мы десериализовали исходный поток, он работает.

Наши методы:

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

Наш пример кода с использованием этих двух:

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 быть MemoryStrea?
Эсан

Ответы:


60

Это так часто, но так глубоко неправильно. Данные Protobuf не являются строковыми данными. Это конечно не ASCII. Вы используете кодирование в обратном направлении . Передача кодировки текста:

  • произвольная строка в форматированных байтах
  • отформатированные байты в исходную строку

У вас нет "отформатированных байтов". У вас есть произвольные байты . Вам нужно использовать что-то вроде base-n (обычно: base-64) кодирования. Это переводы

  • произвольные байты в форматированную строку
  • отформатированная строка в исходные байты

посмотрите на Convert.ToBase64String и Convert. FromBase64String


1
Не могли бы вы использовать BinaryFormatter, похожий на этот странный пример ?
drzaus

@drzaus хм ... может быть и нет:> "Любые непарные суррогатные символы теряются в двоичной сериализации"
drzaus

211

Я только что проверил это и работает отлично.

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

Если streamэто уже было написано, вы, возможно, захотите начать сначала, прежде чем прочитать текст:stream.Seek(0, SeekOrigin.Begin);


И не забудьте использовать блок вокруг StreamReader reader = new StreamReader (stream);
PRMan

7

преобразование MemoryStream в строку: UTF8:

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

2
Вместо этого используйте ToArray (). Буфер может быть больше, чем размер используемых данных. ToArray () возвращает копию данных с правильным размером. var array = stream.ToArray(); var str = Encoding.UTF8.GetString(array, 0, array.Length);, См. Также msdn.microsoft.com/en-us/library/…
Мортеннобель

1
@Mortennobel ToArray()выделяет новый массив в памяти и копирует данные из буфера, что может иметь серьезные последствия, если вы имеете дело с большим количеством данных.
Леви Ботельо,

Обратите внимание на использование stream.Length, а не stream.GetBuffer (). Length. И Леви правильно заметил причину отказа от использования ToArray ().
Вольфганг Гринфельд

5

При тестировании попробуйте использовать UTF8поток кодирования, как показано ниже

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


2

Я написал полезный метод для вызова любого действия, которое принимает StreamWriterи записывает его вместо строки. Метод похож на это;

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

И вы можете использовать это так;

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

Я знаю, что это можно сделать намного проще с помощью StringBuilder, дело в том, что вы можете вызвать любой метод, который принимает StreamWriter.


1

Я хочу сериализовать объекты в строки и обратно.

В отличие от других ответов, но наиболее простым способом сделать это для большинства типов объектов является 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
        }

Все ваши открытые свойства поддерживаемых типов будут сериализованы. Даже некоторые структуры коллекций поддерживаются и будут туннелировать до свойств подобъекта. Вы можете контролировать, как сериализация работает с атрибутами в ваших свойствах.

Это не работает со всеми типами объектов, некоторые типы данных не поддерживаются для сериализации, но в целом это довольно мощный инструмент, и вам не нужно беспокоиться о кодировании.


0

В случае, когда вы хотите сериализовать / десериализовать POCO, JSON-библиотека Newtonsoft действительно хороша. Я использую его для сохранения POCO в SQL Server в виде строк JSON в поле nvarchar. Предостережение заключается в том, что, поскольку он не является истинной де / сериализацией, он не будет сохранять частные / защищенные члены и иерархию классов.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.