2011-03-18 11 views
5

Me gustaría tener un modelo de matriz de objetos para ser serializados a una secuencia binaria. La clase de modelo tendrá principalmente propiedades de cadena y entero.Formato de archivo binario con 1000s de registros en C#

Creo que puedo marcar la clase como [Serializable] y usar el formateador binario; sin embargo, me interesaría saber si cree que es la mejor manera, teniendo en cuenta que mi prioridad es tener un archivo más pequeño como sea posible para la transferencia a través de una conexión de bajo ancho de banda (también puedo comprimir/descomprimir el archivo).

El archivo puede tener miles de registros, por lo que me gustaría poder anexar al disco y leer desde el disco grabar por registro, sin tener que tener todo el archivo en la memoria a la vez.

Así que mis prioridades son: tamaño de archivo pequeño y uso eficiente de la memoria.

¿Tal vez hay un marco preescrito para esto? ¡Parece fácil de hacer con archivos XML y CSV! Es de esperar que sea con un formato binario personalizado también.

gracias

Respuesta

6

Sugiero protobuf.net que es muy eficiente.

Habiendo dicho esto, esto no podrá manejar la serialización/deserialización de objetos individuales en su colección. Esa parte debes implementarla tú mismo.

  • Una solución es: Almacenar objetos como archivos individuales en una carpeta. El nombre del archivo contendrá una referencia para que, según el nombre, pueda encontrar el objeto que necesita.

  • Otra es tener un archivo pero mantener un archivo de índice que mantiene una lista de todos los objetos y sus posiciones en el archivo. Esto es mucho más complicado ya que cuando está guardando un objeto que está en el medio del archivo, tiene que mover todas las otras direcciones, y tal vez un b-tree es más efectivo.

+2

Para la segunda opción: [FileDB] (http://nuget.org/List/Packages/FileDB). –

+0

Gracias Martinho. ¡Me gustó FileDB! – Aliostad

1

Puede utilizar el BinaryFormatter. Es una buena solución para querer un archivo pequeño, pero solo usted sabe si es la mejor solución para su dominio. Sin embargo, no creo que puedas leer un registro a la vez.

El único código de ejemplo que tengo en este momento es para DataSet. Estos métodos de extensión (de) serializarán un DataSet personalizado, que, si no recuerdo mal, era la forma más fácil de tener un tipo que puede usar 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); 
} 

También puede echar un vistazo a la DataContractSerializer, que es nueva forma de .NET 'estándar' de hacer frente a la serialización (de acuerdo con C# 4.0 en pocas palabras, Albahari & Albahari). En ese caso, también querrá leer Best Practices: Data Contract Versioning. A continuación hay ejemplos de cómo (de) serializar en XML y JSON, aunque no serían directamente aplicables a su situación (ya que quería archivos pequeños). Pero podrías comprimir los archivos.

/// <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); 
} 
1

Yo recomendaría el uso de SQL Server Compact para almacenar sus objetos como objetos sin serializar, es muy ligero y extremadamente rápido, lo usé bajo alta carga útil en el servicio a una gran cantidad de solicitudes en el servidor.

Tampoco te recomiendo que guardes tus datos en formato binario (serializado) porque sería un gran dolor cuando se trata de cambiar los objetos que vas a almacenar. También es doloroso si tiene que ver lo que está almacenando, porque debe deserializar toda la colección.

En cuanto al envío, prefiero utilizar la serialización XML con compresión zip si es necesario. El formato XML hace que la depuración sea mucho más fácil si necesita ver lo que está enviando o hacer algunas pruebas.

0

Si quiere que sea pequeño hágalo usted mismo. Asegúrese de almacenar solo los datos que necesita. Por ejemplo, si solo tiene 255 valores diferentes, use un byte.

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

casi siempre utilizan una estructura tan simple como este para almacenar los datos

id (ushort)

DATA_SIZE (uint)

de datos de tamaño DATA_SIZE

Almacene solo la información que debe tener y no piense cómo se va a usar. Cuando lo cargas, consideras cómo quieres usar los datos.

0

Estaría tentado de seguir con BinaryFormatter para los objetos en sí, o tal vez protobuf.net como se sugiere en otros lugares.

Si el aspecto de acceso aleatorio es muy importante (leer y anexar registro por registro) es posible que desee crear un archivo zip (o similar) que contenga un archivo de índice y cada objeto serializado en su propio archivo en el zip (o tal vez en pequeñas colecciones).

De esta forma, puede tener efectivamente un mini sistema de archivos comprimido y acceder individualmente a sus registros.

2

Otra opción es simplemente serializar a un formato de archivo de texto de ancho fijo y dejar que ZIP maneje la compresión. El ancho fijo significa que puede usar fácilmente un MemoryMappedFile para recorrer cada registro sin necesidad de cargar todo el archivo en la memoria.

Cuestiones relacionadas