C#二进制序列化逐行写入文件,或如何分隔

3
我在运行时有一些对象集合,已经可序列化,我需要将对象的状态持久化到文件中。我使用BinaryFormatter进行快速编码并将序列化的对象保存到文件中。
我想可以每行保存一个对象。但是当我在记事本中打开文件时,它比一行长。无法滚动。如何将二进制序列化的对象存储为每行一个?
我知道我可以在每个对象之后使用分隔符,以便在将它们读回应用程序时,我可以知道对象的结尾。根据信息论,这会增加数据的大小(Sipser书)。有什么最好的算法可以提供一个不会破坏信息的分隔符?
除了二进制序列化外,您认为Json格式更可行吗?我可以逐行存储实体的Json格式吗?
此外,序列化/反序列化会引入开销,影响性能。Json会更快吗?
有什么想法?
谢谢。
4个回答

6

序列化函数像一个FIFO队列,您不需要读取文件的部分,因为格式化程序会代替您完成,您只需要知道您推入对象的顺序。

public class Test
    {

        public void testSerialize()
        {
            TestObj obj = new TestObj();
            obj.str = "Some String";
            IFormatter formatter = new BinaryFormatter();
            Stream stream = new FileStream("MyFile.bin", FileMode.Create, FileAccess.Write, FileShare.None);
            formatter.Serialize(stream, obj);
            formatter.Serialize(stream, 1);
            formatter.Serialize(stream, DateTime.Now);
            stream.Close();
        }

        public void TestDeserialize()
        {
            Stream stream = new FileStream("MyFile.bin", FileMode.Open, FileAccess.Read, FileShare.None);
            IFormatter formatter = new BinaryFormatter();
            TestObj obj = (TestObj)formatter.Deserialize(stream);
            int obj2 = (int)formatter.Deserialize(stream);
            DateTime dt = (DateTime)formatter.Deserialize(stream);
            stream.Close();
        }
    }

    [Serializable]
    class TestObj
    {
        public string str = "1";
        int i = 2;
    }

有趣。我应该试一下。 - DarthVader
好的问题:你如何知道文件中有多少个对象?你打算如何填充它们? - DarthVader
1
实际上你无法知道有多少个对象,因为不同类型的对象可能在其中,但是如果你知道只有类型X的对象在里面,你可以在循环中反序列化并将对象放入列表中,在通过流的末尾时捕获一个Serialization异常,这意味着你已经完成了。但这种方法容易出错,因为你必须依赖于异常消息“在解析完成之前遇到了流的末尾”。 - Marino Šimić
1
@user177883 要么确保你序列化的第一个对象是一个包含对象数量的头部(例如 int),要么在读取完每个对象后检查 EOF。 - Justin
1
是的,很好的想法+1,你可以把计数放在第一位 :) - Marino Šimić
@Marino,这就是我现在正在尝试的。@Kragen,确实。 - DarthVader

1

嗯,

序列化/反序列化会引入开销,使用Json会更快吗?

JSON仍然是一种形式的序列化,不,它可能不会比二进制序列化更快——二进制序列化旨在紧凑和快速,而JSON序列化更注重可读性,因此可能会更慢,很可能不够紧凑。

你可以单独对每个对象进行序列化,并在每个对象之间发出一些分隔符(例如换行符),但我不知道你可以使用什么分隔符来保证不会出现在序列化数据中(如果你序列化一个包含换行符的字符串会发生什么?)。

如果你使用.Net序列化框架发出的分隔符,那么显然你将使确定对象之间的断点变得困难(如果不是不可能),从而导致反序列化失败。

你到底想把每个对象放在自己的一行上有什么特别的原因吗?


所以我可以逐行读取对象,而不需要分隔符。 - DarthVader
@user177883 为什么不直接序列化对象数组呢? - Justin
可能行。好主意。我应该将所有对象存储在列表中,然后将列表序列化到文件中吗? - DarthVader
@user177883 这是我可能会做的事情,或者如果你想避免一次性反序列化整个列表,那就按照Marino的建议去做。 - Justin
Marino 创建了 3 个对象并读取了 3 个对象。在运行时,我不知道我有多少个对象。 - DarthVader
1
@user177883 你可以先将一个头对象(例如一个 int)序列化到文件中,其中包含保存的对象数量 - 在读取时,确保首先从堆栈中弹出/反序列化 int,然后您就会知道要反序列化多少个对象。当您完成反序列化时,还可以检查是否已到达文件结尾(假设您没有在文件末尾序列化任何额外数据)。 - Justin

1
二进制序列化将数据保存为任意字节;这些字节可以包含换行符。
你要求使用换行符作为分隔符。换行符与其他分隔符没有区别;它们也会增加数据的大小。

0
你也可以创建一个 ArrayList 并将对象添加到其中,然后对其进行序列化 ;)
ArrayList list = new ArrayList();
list.Add(1);
list.Add("Hello World");
list.Add(DateTime.Now);

BinaryFormatter bf = new BinaryFormatter();

FileStream fsout = new FileStream("file.dat", FileMode.Create);
bf.Serialize(fsout, list);
fsout.Close();

FileStream fsin = new FileStream("file.dat", FileMode.Open);
ArrayList list2 = (ArrayList)bf.Deserialize(fsin);

fsin.Close();

foreach (object o in list2)
   Console.WriteLine(o.GetType());

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接