将Stream转换为String并返回……我们缺少什么?


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
流不应该是MemoryStrea吗?
Ehsan

Answers:


60

这是很普遍的,但却是非常错误的。Protobuf数据不是字符串数据。当然不是ASCII。您正在反向使用编码。文本编码传输:

  • 任意字符串以格式化字节
  • 格式化字节到原始字符串

您没有“格式化字节”。您有任意字节。您需要使用类似base-n(通常是base-64)编码的方法。此转移

  • 任意字节到格式化的字符串
  • 格式化为原始字节的字符串

看一下Convert.ToBase64String和Convert。FromBase64String


1
您能否使用BinaryFormatter类似于此奇怪示例的
drzaus 2014年

@drzaus hm ...也许不是:>“任何未配对的代理字符在二进制序列化中都会丢失”
drzaus 2014年

210

我刚刚测试过,效果很好。

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);周围使用using块;
PRMan

7

UTF8 MemoryStream到String的转换:

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/...
Mortennobel

1
@Mortennobel ToArray()在内存中分配一个新数组并从缓冲区复制数据,如果您要处理大量数据,这可能会产生严重的影响。
Levi Botelho

注意使用stream.Length而不是stream.GetBuffer()。Length。Levi正确地指出了不使用ToArray()的原因。
沃尔夫冈·格林菲尔德

5

测试时,请尝试使用UTF8如下所示的编码流

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

5

试试这个。

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

2

我写了一个有用的方法来调用需要a的任何动作,并将其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);

我知道使用a可以轻松完成这StringBuilder一点,关键是您可以调用采用a的任何方法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的用例中,Newtonsoft的JSON库非常好。我使用它在nvarchar字段中将POCO作为JSON字符串持久存储在SQL Server中。需要注意的是,由于它不是真正的反序列化,因此不会保留私有/受保护的成员和类层次结构。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.