Konversi Stream ke String dan kembali ... apa yang kita lewatkan?

162

Saya ingin membuat serial objek untuk string, dan kembali.

Kami menggunakan protobuf-net untuk mengubah objek menjadi Stream dan kembali, berhasil.

Namun, Streaming ke string dan kembali ... tidak begitu sukses. Setelah melalui StreamToStringdan StringToStream, yang baru Streamtidak deserialized oleh protobuf-net; itu memunculkan Arithmetic Operation resulted in an Overflowpengecualian. Jika kami membatalkan deserialisasi aliran asli, itu berfungsi.

Metode kami:

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

Contoh kode kami menggunakan dua ini:

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
sumber
1
bukankah seharusnya Stream menjadi MemoryStrea?
Ehsan

Jawaban:

60

Ini sangat umum tetapi sangat salah. Data protobuf bukan data string. Jelas bukan ASCII. Anda menggunakan penyandian mundur . Pengkodean teks transfer:

  • string arbitrer untuk byte yang diformat
  • byte diformat ke string asli

Anda tidak memiliki "byte yang diformat". Anda memiliki byte yang berubah - ubah . Anda perlu menggunakan sesuatu seperti pengkodean base-n (umumnya: base-64). Ini transfer

  • byte sewenang-wenang ke string yang diformat
  • string yang diformat ke byte asli

lihat Convert.ToBase64String dan Konversi. FromBase64String

Marc Gravell
sumber
1
Bisakah Anda menggunakan BinaryFormatter, mirip dengan contoh aneh ini ?
drzaus
@drzaus hm ... mungkin tidak:> "Setiap karakter pengganti yang tidak berpasangan hilang dalam serialisasi biner"
drzaus
210

Saya baru saja menguji ini dan berfungsi dengan baik.

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

Jika streamsudah ditulis, Anda mungkin ingin mencari dari awal sebelum yang pertama sebelum membacakan teks:stream.Seek(0, SeekOrigin.Begin);

Ehsan
sumber
Dan jangan lupa blok penggunaan di sekitar StreamReader reader = new StreamReader (stream);
PRMan
7

konversi UTF8 MemoryStream ke String:

var res = Encoding.UTF8.GetString(stream.GetBuffer(), 0 , (int)stream.Length)
Wolfgang Grinfeld
sumber
2
Gunakan ToArray () sebagai gantinya. Buffer mungkin lebih besar dari ukuran data yang digunakan. ToArray () mengembalikan salinan data dengan ukuran yang benar. var array = stream.ToArray(); var str = Encoding.UTF8.GetString(array, 0, array.Length);. Lihat juga msdn.microsoft.com/en-us/library/…
Mortennobel
1
@Mortennobel ToArray()mengalokasikan array baru dalam memori dan menyalin data dari buffer, yang mungkin memiliki implikasi serius jika Anda berurusan dengan banyak data.
Levi Botelho
Perhatikan penggunaan stream.Length, bukan stream.GetBuffer (). Panjang. Dan Levi benar mencatat alasan untuk tidak menggunakan ToArray ().
Wolfgang Grinfeld
5

Saat Anda menguji coba dengan UTF8Encode stream seperti di bawah ini

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

Coba ini.

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

Saya menulis metode yang berguna untuk memanggil tindakan apa pun yang mengambil StreamWriterdan menuliskannya ke string. Metodenya seperti ini;

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

Dan Anda bisa menggunakannya seperti ini;

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

Saya tahu ini bisa dilakukan lebih mudah dengan StringBuilder, intinya adalah bahwa Anda dapat memanggil metode apa saja yang membutuhkan StreamWriter.

Steztric
sumber
1

Saya ingin membuat serial objek untuk string, dan kembali.

Berbeda dari jawaban lain, tetapi cara paling mudah untuk melakukan hal itu untuk sebagian besar tipe objek adalah 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
        }

Semua properti publik Anda dari jenis yang didukung akan diserialisasi. Bahkan beberapa struktur koleksi didukung, dan akan terowongan ke properti sub-objek. Anda dapat mengontrol cara kerja serialisasi dengan atribut di properti Anda.

Ini tidak bekerja dengan semua jenis objek, beberapa tipe data tidak didukung untuk serialisasi, tetapi secara keseluruhan cukup kuat, dan Anda tidak perlu khawatir tentang pengkodean.

Denise Skidmore
sumber
0

Di usecase di mana Anda ingin membuat serial / deserialize POCOs, pustaka JSON Newtonsoft sangat bagus. Saya menggunakannya untuk bertahan POCO dalam SQL Server sebagai string JSON di bidang nvarchar. Peringatan adalah bahwa karena itu tidak benar de / serialisasi, itu tidak akan melestarikan anggota pribadi / dilindungi dan hirarki kelas.

MD Luffy
sumber