C#中包含1000条记录的二进制文件格式

时间:2011-03-18 15:31:27

标签: c# .net binary binaryfiles binaryformatter

我想将一个数组模型对象序列化为二进制流。模型类主要有字符串和整数属性。

我相信我可以将该类标记为[Serializable]并使用二进制格式化程序,但是我有兴趣知道您是否认为这是最好的方法,请记住我的优先级是让文件小到可以通过低带宽连接进行传输(我也可以压缩/解压缩文件)。

该文件可能有1000条记录,所以理想情况下我希望能够附加到磁盘并按记录读取磁盘记录,而不必一次将整个文件放在内存中。

所以我的优先事项是:文件小,内存使用效率高。

也许有预先编写的框架?使用XML和CSV文件似乎很容易!希望它也是一种自定义二进制格式。

感谢

6 个答案:

答案 0 :(得分:6)

我建议protobuf.net非常有效。

话虽如此,这将无法处理您的集合中的单个对象的序列化/解除序列。那部分你需要自己实现。

  • 一种解决方案是:将对象存储为文件夹中的单个文件。文件名将包含一个引用,以便根据名称,您可以找到所需的对象。

  • 另一个是拥有一个文件,但保留一个索引文件,该文件保存所有对象及其在文件中的位置的列表。这要复杂得多,因为当您保存位于文件中间的对象时,您必须移动所有其他地址,并且b树可能更有效。

答案 1 :(得分:2)

另一个选择是序列化为固定宽度的文本文件格式,让ZIP处理压缩。固定宽度意味着您可以轻松使用MemoryMappedFile遍历每条记录,而无需将整个文件加载到内存中。

答案 2 :(得分:1)

您可以使用BinaryFormatter。这是一个很好的解决方案,想要一个小文件,但只有你知道它是否是你的域的最佳解决方案。不过,我认为你不能一次阅读一条记录。

我目前唯一的示例代码是DataSet。这些扩展方法将(de)序列化自定义DataSet,如果我没记错的话,这是获得可以使用BinaryFormatter的类型的最简单方法。

public static TDataSet LoadBinary<TDataSet>(Stream stream) where TDataSet : DataSet
{
    var formatter = new BinaryFormatter();
    return (TDataSet)formatter.Deserialize(stream);
}

public static void WriteBinary<TDataSet>(this TDataSet dataSet, Stream stream) where TDataSet : DataSet
{
    dataSet.RemotingFormat = SerializationFormat.Binary;
    var formatter = new BinaryFormatter();
    formatter.Serialize(stream, dataSet);
}

您可能还会看一下DataContractSerializer,这是.NET处理序列化的新“标准”方式(根据C#4.0 In A Nutshell,Albahari&amp; Albahari)。在这种情况下,您还需要阅读Best Practices: Data Contract Versioning。下面是如何(de)序列化XML和JSON的示例,即使它们不能直接适用于您的情况(因为您需要小文件)。但你可以压缩文件。

/// <summary>
/// Converts this instance to XML using the <see cref="DataContractSerializer"/>.
/// </summary>
/// <typeparam name="TSerializable">
/// A type that is serializable using the <see cref="DataContractSerializer"/>.
/// </typeparam>
/// <param name="value">
/// The object to be serialized to XML.
/// </param>
/// <returns>
/// Formatted XML representing this instance. Does not include the XML declaration.
/// </returns>
public static string ToXml<TSerializable>(this TSerializable value)
{
    var serializer = new DataContractSerializer(typeof(TSerializable));
    var output = new StringWriter();
    using (var writer = new XmlTextWriter(output) { Formatting = Formatting.Indented })
    {
        serializer.WriteObject(writer, value);
    }
    return output.GetStringBuilder().ToString();
}

/// <summary>
/// Converts this instance to XML using the <see cref="DataContractSerializer"/> and writes it to the specified file.
/// </summary>
/// <typeparam name="TSerializable">
/// A type that is serializable using the <see cref="DataContractSerializer"/>.
/// </typeparam>
/// <param name="value">
/// The object to be serialized to XML.
/// </param>
/// <param name="filePath">Path of the file to write to.</param>
public static void WriteXml<TSerializable>(this TSerializable value, string filePath)
{
    var serializer = new DataContractSerializer(typeof(TSerializable));
    using (var writer = XmlWriter.Create(filePath, new XmlWriterSettings { Indent = true }))
    {
        serializer.WriteObject(writer, value);
    }
}

/// <summary>
/// Creates from an instance of the specified class from XML.
/// </summary>
/// <typeparam name="TSerializable">The type of the serializable object.</typeparam>
/// <param name="xml">The XML representation of the instance.</param>
/// <returns>An instance created from the XML input.</returns>
public static TSerializable CreateFromXml<TSerializable>(string xml)
{
    var serializer = new DataContractSerializer(typeof(TSerializable));

    using (var stringReader = new StringReader(xml))
    using (var reader = XmlReader.Create(stringReader))
    {
        return (TSerializable)serializer.ReadObject(reader);
    }
}

/// <summary>
/// Creates from an instance of the specified class from the specified XML file.
/// </summary>
/// <param name="filePath">
/// Path to the XML file.
/// </param>
/// <typeparam name="TSerializable">
/// The type of the serializable object.
/// </typeparam>
/// <returns>
/// An instance created from the XML input.
/// </returns>
public static TSerializable CreateFromXmlFile<TSerializable>(string filePath)
{
    var serializer = new DataContractSerializer(typeof(TSerializable));

    using (var reader = XmlReader.Create(filePath))
    {
        return (TSerializable)serializer.ReadObject(reader);
    }
}

public static T LoadJson<T>(Stream stream) where T : class
{
    var serializer = new DataContractJsonSerializer(typeof(T));
    object readObject = serializer.ReadObject(stream);
    return (T)readObject;
}

public static void WriteJson<T>(this T value, Stream stream) where T : class
{
    var serializer = new DataContractJsonSerializer(typeof(T));
    serializer.WriteObject(stream, value);
}

答案 3 :(得分:1)

我建议使用Sql Server Compact将对象存储为对象而不进行序列化,它非常轻量级且速度极快,我在高负载下使用它来为服务器上的大量请求提供服务。

我也不建议以二进制格式存储数据(序列化),因为在更改要存储的对象时会非常痛苦。如果你必须看到你正在存储的内容,那也很痛苦,因为你必须反序列化整个集合。

至于发送,如果需要,我更喜欢使用带有zip压缩的XML序列化。如果您需要查看发送的内容或进行一些测试,XML格式可以使调试变得更加容易。

答案 4 :(得分:0)

如果你想让它变小,你自己动手吧。确保只存储您需要的数据。例如,如果只有255个不同的值,则使用一个字节。

http://msdn.microsoft.com/en-us/library/system.bitconverter.aspx

我几乎总是使用这样的简单结构来存储数据

id(ushort)

data_size(uint)

数据大小data_size

只存储您必须拥有的信息,不要考虑如何使用它。当您加载它时,您会考虑如何使用数据。

答案 5 :(得分:0)

我很想坚持使用BinaryFormatter作为对象本身,或者像其他地方建议的那样使用protobuf.net。

如果随机访问方面非常重要(按记录阅读和追加记录),您可能需要查看创建包含索引文件的zip文件(或类似文件),并将每个对象序列化为zip文件中的自己文件(或者也许是小集合)。

这样,您就可以有效地拥有一个压缩的迷你文件系统,并让您可以单独访问您的记录。