如何访问序列化数组中的单个项目?

问题描述 投票:1回答:2

我想将时间戳数组存储在二进制平面文件中。 我的要求之一是,为了高效的查询目的,我以后可以访问单个时间戳,而不必先读取和反序列化整个数组(我使用二进制搜索算法来查找开始时间戳和结束时间戳的文件位置这又决定了在这两个时间戳之间读取和反序列化的字节,因为整个二进制文件的大小可以是多个GB。]

显然,简单但缓慢的方法是使用BitConverter.GetBytes(timestamp)将每个时间戳转换为字节,然后将其存储在文件中。然后,我可以分别访问文件中的每个项目,并使用我的自定义二进制搜索算法来查找与所需时间戳匹配的时间戳。

但是,我发现在值类型数组的序列化/反序列化方面,BinaryFormatter的效率极高(比protobuf-net和我尝试过的任何其他序列化器快好几倍)。因此,我尝试尝试将时间戳数组序列化为二进制形式。但是,显然,这现在将阻止我访问文件中的各个时间戳,而不必先反序列化整个数组。

通过BinaryFormatter序列化了整个项目数组之后,是否仍然有办法以二进制形式访问单个项目?

这里有一些代码片段演示了我的意思:

var sampleArray = new int[5] { 1,2,3,4,5};

        var serializedSingleValueArray = sampleArray.SelectMany(x => BitConverter.GetBytes(x)).ToArray();
        var serializedArrayofSingleValues = Serializers.BinarySerializeToArray(sampleArray);

        var deserializesToCorrectValue = BitConverter.ToInt32(serializedSingleValueArray, 0); //value = 1 (ok)
        var wrongDeserialization = BitConverter.ToInt32(serializedArrayofSingleValues, 0); //value = 256 (???)

这里是序列化功能:

public static byte[]BinarySerializeToArray(object toSerialize)
    {
        using (var stream = new MemoryStream())
        {
            Formatter.Serialize(stream, toSerialize);
            return stream.ToArray();
        }
    }

编辑:我不需要担心高效的内存消耗或文件大小,因为目前这些还不是瓶颈。序列化和反序列化的速度是数千兆字节大二进制文件以及非常大的原语数组的瓶颈。

c# serialization deserialization binaryformatter bitconverter
2个回答
0
投票

位转换器不是“慢”版本,它只是将所有内容转换为字节[]序列的一种方法。实际上,这并不昂贵,只是以不同的方式解释内存。

计算文件中的位置,加载8个字节,将其转换为DateTime,就完成了。

您只应对简单的结构化文件执行此操作,而对于简单的结构化文件,则不需要二进制格式化程序。只需将一个数组加载/保存到一个文件即可。这样,您可以确定可以计算文件位置。

换句话说。自己保存数组,日期字节日期,然后按日期也可以加载它。

以一种处理方式书写,而以另一种处理方式阅读,总是一个坏主意。


0
投票

如果您的问题仅是“如何将结构数组转换为字节[]”,则除BitConverter以外,还有其他选择。BitConverter用于单个值,Buffer类用于数组。

        double[] d = new double[100];
        d[4] = 1235;
        d[8] = 5678;
        byte[] b = new byte[800];
        Buffer.BlockCopy(d, 0, b, 0, d.Length*sizeof(double));

        // just to test it works
        double[] d1 = new double[100];
        Buffer.BlockCopy(b, 0, d1, 0, d.Length * sizeof(double));

这将执行字节级的复制,而不会进行任何转换,也不会迭代项目。

您可以将此字节数组直接放入流中(不是StreamWriter,不是Formatter)

        stream.Write(b, 0, 800);

这绝对是最快的写入文件的方法,但是它涉及完整的副本,但是可能还包括其他任何可行的方法,都将读取项目,出于某种原因首先将其存储,然后再进入文件。

如果这是唯一写入文件的内容-不需要在文件中写入数组长度,则可以使用文件长度。

要读取文件中的第100个double值:

    file.Seek(100*sizeof(double), SeekOrigin.Begin);
    byte[] tmp = new byte[8];
    f.Read(tmp, 0, 8);
    double value = BitConverter.ToDouble(tmp, 0);

这里,对于单个值,可以使用BitConverter

这是.NET Framework C#<= 7.0的解决方案>>

对于.NET Standard / .NET Core,C#8.0,Span<T>有更多选项,可让您访问内部存储器,而无需复制数据。

© www.soinside.com 2019 - 2024. All rights reserved.