ストリームを文字列に変換して戻す…何が欠けているのですか?


162

オブジェクトを文字列にシリアル化したい。

protobuf-netを使用して、オブジェクトをストリームに変換し、正常に戻します。

ただし、文字列にストリーミングして戻る...それほど成功していません。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);
}

これら2つを使用したコード例:

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にしないでください。
イーサン2013

回答:


60

これは一般的ですが、非常に間違っています。Protobufデータは文字列データではありません。それは確かにASCIIではありません。エンコーディングを逆方向に使用しています。テキストエンコーディングの転送:

  • フォーマットされたバイトへの任意の文字列
  • 元の文字列にフォーマットされたバイト

「フォーマット済みバイト」がありません。あなたは任意のバイトを持っています。base-n(通常:base-64)エンコードのようなものを使用する必要があります。この転送

  • フォーマットされた文字列への任意のバイト
  • 元のバイトにフォーマットされた文字列

Convert.ToBase64StringとConvertを見てください。FromBase64String


1
BinaryFormatterこの奇妙な例に似たを使用できますか?
drzaus 14年

@drzaus hm ...たぶんそうではない:>「バイナリシリアル化でペアになっていないサロゲート文字が失われる」
drzaus '20

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リーダーを囲むusingブロックを忘れないでください= new StreamReader(stream);
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.GetBuffer()。Lengthではなく、stream.Lengthの使用に注意してください。そして、LeviはToArray()を使用しない理由を正しく述べました。
ヴォルフガンググリンフェルド

5

あなたがテストするときUTF8、以下のようなエンコードストリームで試してください

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


2

StreamWriter代わりに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);
    }
}

そして、あなたはこのようにそれを使うことができます。

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をシリアライズ/デシリアライズするユースケースでは、NewtonsoftのJSONライブラリは非常に優れています。私はそれを使用して、SQL Server内のPOCOをnvarcharフィールドのJSON文字列として永続化します。警告は、真のデシリアライズではないため、プライベート/保護されたメンバーとクラス階層を保持しないということです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.