Двоичный формат файла с тысячами записей в C # - PullRequest
5 голосов
/ 18 марта 2011

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

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

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

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

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

спасибо

Ответы [ 6 ]

6 голосов
/ 18 марта 2011

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

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

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

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

2 голосов
/ 18 марта 2011

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

1 голос
/ 18 марта 2011

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

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

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

1 голос
/ 18 марта 2011

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

Единственный пример кода, который у меня есть на данный момент, относится к DataSet .Эти методы расширения (де) сериализуют пользовательский 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 в 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);
}
0 голосов
/ 18 марта 2011

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

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

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

0 голосов
/ 18 марта 2011

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

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

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

id (ushort)

data_size (uint)

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

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

...