Двоичный формат файла с тысячами записей в C#

Я хотел бы, чтобы объекты модели массива были сериализованы в двоичный поток. Класс модели в основном будет иметь строковые и целочисленные свойства.

Я считаю, что могу пометить класс как [Serializable] и использовать двоичный форматтер, однако мне было бы интересно узнать, считаете ли вы, что это лучший способ, учитывая, что мой приоритет - иметь как можно меньший файл для передачи через соединение с низкой пропускной способностью (я также могу заархивировать / разархивировать файл).

Файл может иметь 1000 записей, поэтому в идеале я хотел бы иметь возможность добавлять к диску и читать с диска запись за записью, без необходимости одновременного хранения всего файла в памяти.

Итак, мои приоритеты: небольшой размер файла и эффективное использование памяти.

Может быть, для этого есть готовый фреймворк? Кажется, это легко сделать с файлами XML и CSV! Надеюсь, это тоже с пользовательским двоичным форматом.

Благодарность


person krisdyson    schedule 18.03.2011    source источник


Ответы (6)


Я предлагаю protobuf.net, что очень эффективно.

Сказав это, это не сможет обрабатывать сериализацию/десериализацию отдельных объектов в вашей коллекции. Эту часть вам нужно реализовать самостоятельно.

  • Одним из решений является: Хранить объекты в виде отдельных файлов в папке. Имя файла будет содержать ссылку, чтобы по имени можно было найти нужный объект.

  • Другой вариант — иметь один файл, но иметь индексный файл, в котором хранится список всех объектов и их позиций в файле. Это намного сложнее, так как когда вы сохраняете объект, который находится в середине файла, вам нужно переместить все остальные адреса, и, возможно, B-дерево более эффективно.

person Aliostad    schedule 18.03.2011
comment
Спасибо Мартиньо. Мне понравился FileDB! - person Aliostad; 18.03.2011

Другой вариант — просто сериализовать в формат текстового файла с фиксированной шириной и позволить ZIP обрабатывать сжатие. Фиксированная ширина означает, что вы можете легко использовать MemoryMappedFile для просмотра каждой записи без необходимости загрузки всего файла в память.

person Chris Haas    schedule 18.03.2011

Я бы рекомендовал использовать Sql Server Compact для хранения ваших объектов в виде объектов без сериализации, он довольно легкий и очень быстрый, я использовал его при высокой полезной нагрузке при обслуживании большого количества запросов на сервере.

Я также не рекомендую хранить ваши данные в двоичном формате (сериализованном), потому что было бы ужасно сложно изменить объекты, которые вы собираетесь хранить. Это также болезненно, если вам нужно видеть, что вы храните, потому что вам нужно десериализовать всю коллекцию.

Что касается отправки, я предпочитаю использовать XML-сериализацию с zip-сжатием, если это необходимо. Формат XML значительно упрощает отладку, если вам нужно посмотреть, что вы отправляете, или выполнить некоторые тесты.

person Dmitrii Lobanov    schedule 18.03.2011

Вы можете использовать BinaryFormatter. . Это хорошее решение для небольшого файла, но только вы знаете, является ли это лучшим решением для вашего домена. Я не думаю, что вы можете читать одну запись за раз.

На данный момент у меня есть только один пример кода для 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 & Albahari). В этом случае вам также следует прочитать Рекомендации: управление версиями контрактов данных. Ниже приведены примеры (де)сериализации в 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);
}
person Pat    schedule 18.03.2011

Если вы хотите, чтобы он был маленьким, сделайте это сами. Убедитесь, что вы сохраняете только те данные, которые вам нужны. Например, если у вас есть только 255 различных значений, используйте байт.

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

Я почти всегда использую простую структуру, подобную этой, для хранения данных.

идентификатор (короткий)

размер_данных (uint)

данные размера data_size

Храните только ту информацию, которая вам необходима, и не думайте о том, как она будет использоваться. Когда вы загружаете его, вы думаете, как вы хотите использовать данные.

person madmik3    schedule 18.03.2011

У меня возникло бы искушение использовать BinaryFormatter для самих объектов или, возможно, protobuf.net, как это предлагается в другом месте.

Если аспект произвольного доступа очень важен (чтение и добавление записи за записью), вы можете захотеть создать zip-файл (или аналогичный), содержащий индексный файл, и каждый объект, сериализованный в свой собственный файл в zip (или, возможно, в небольших коллекциях).

Таким образом, вы можете эффективно использовать мини-файловую систему, которая сжата и дает вам доступ к вашим записям индивидуально.

person Rob Levine    schedule 18.03.2011