2010-08-04 27 views
33

Estoy creando un método en C# que genera un archivo de texto para Google Product Feed. El feed contendrá más de 30,000 registros y el archivo de texto pesa actualmente a ~ 7Mb.¿Cómo escribir eficientemente un archivo de texto grande en C#?

Aquí está el código que estoy usando actualmente (algunas líneas se eliminan por motivos de brevedad).

public static void GenerateTextFile(string filePath) { 

    var sb = new StringBuilder(1000); 
    sb.Append("availability").Append("\t"); 
    sb.Append("condition").Append("\t"); 
    sb.Append("description").Append("\t"); 
    // repetitive code hidden for brevity ... 
    sb.Append(Environment.NewLine); 

    var items = inventoryRepo.GetItemsForSale(); 

    foreach (var p in items) { 
    sb.Append("in stock").Append("\t"); 
    sb.Append("used").Append("\t"); 
    sb.Append(p.Description).Append("\t"); 
    // repetitive code hidden for brevity ... 
    sb.AppendLine(); 
    } 

    using (StreamWriter outfile = new StreamWriter(filePath)) { 
     result.Append("Writing text file to disk.").AppendLine(); 
     outfile.Write(sb.ToString()); 
    } 
} 

Me pregunto si StringBuilder es la herramienta adecuada para el trabajo. ¿Habría ganancias de rendimiento si usara un TextWriter en su lugar?

No conozco mucho sobre el rendimiento de IO, por lo que cualquier ayuda o mejora general sería apreciada. Gracias.

+0

Desde el momento en que escribí esta pregunta, el proyecto Linq2Csv cobró vida. Es una forma mucho mejor de manejar el código que estaba escribiendo. http://nuget.org/packages/LinqToCsv – jessegavin

+0

¿Algún código fuente completo con solución? – Kiquenet

+0

Lo siento, fue escrito para uno de mis clientes. Deberías mirar realmente en Linq2Csv. Hará que este tipo de cosas sea mucho más fácil. – jessegavin

Respuesta

61

Las operaciones de E/S de archivos generalmente están bien optimizadas en los sistemas operativos modernos. No intente ensamblar toda la cadena del archivo en memoria ... solo escríbalo pieza por pieza. El FileStream se ocupará del almacenamiento en búfer y otras consideraciones de rendimiento.

Usted puede hacer este cambio fácilmente moviendo:

using (StreamWriter outfile = new StreamWriter(filePath)) { 

a la parte superior de la función, y deshacerse de la StringBuilder escrito directamente al archivo en su lugar.

Hay varias razones por las que debe evitar la creación de grandes cadenas en la memoria:

  1. sino que puede desempeñarse peor, porque el StringBuilder tiene que aumentar su capacidad a medida que se escribe en él, lo que resulta en la reasignación y copia de la memoria.
  2. Puede requerir más memoria de la que puede asignar físicamente, lo que puede resultar en el uso de la memoria virtual (el archivo de intercambio) que es mucho más lenta que la RAM.
  3. Para archivos verdaderamente grandes (> 2Gb) se le agotará el espacio de direcciones (en plataformas de 32 bits) y nunca se completará.
  4. Para escribir el contenido StringBuilder en un archivo, debe usar ToString(), que efectivamente duplica el consumo de memoria del proceso, ya que ambas copias deben estar en la memoria durante un período de tiempo. Esta operación también puede fallar si su espacio de direcciones está suficientemente fragmentado, de modo que no se puede asignar un solo bloque contiguo de memoria.
+0

Buena respuesta. Se puede intentar sintonizar usando la sobrecarga del constructor de StreamWriter que le permite definir el tamaño del buffer ... –

+0

¡Hola, gracias por su respuesta! Aprecio que se tome el tiempo de agregar más explicaciones sobre cómo manejar este tipo de situaciones. – jessegavin

+0

5 años después ... ¿la clase 'FileStream' sigue siendo el mejor método para escribir archivos de texto ~ 7MB? – n00dles

10

Escriba una cadena a la vez utilizando StreamWriter.Write en lugar de almacenar todo en un StringBuilder.

+4

Realmente espero que no signifique que escriba un * bit * a la vez. –

+0

@JSBangs - lol - modificado. –

+0

Si bien esta fue una buena respuesta. Tengo un archivo de aproximadamente 20Mb de tamaño y el problema al que me enfrento es que StreamWriter puso en realidad un retorno de carro/una nueva línea al final. Estoy tratando de eliminar ese retorno de carro adicional al final y como ya se señaló, StringBuilder no es una gran solución para el rendimiento o el tamaño. Intenté StreamReader.Peek() para mirar la línea antes de que llegue al final. Alguna idea? – petersmm

24

Simplemente mueva la instrucción using para que abarque la totalidad de su código y escriba directamente en el archivo. No veo el punto de mantener todo en la memoria primero.

Cuestiones relacionadas