2011-01-20 23 views
13

Escenario: archivo de texto de 150 MB que es la bandeja de entrada exportada de una cuenta de correo electrónico anterior. Es necesario analizar y extraer correos electrónicos de un usuario específico y los escribe en un archivo nuevo y único. Tengo un código que funciona, es obstinado lento.Optimizar archivo C# IO

Estoy usando cadenas de marcador para buscar dónde comenzar/finalizar la copia desde el archivo original.

Aquí es la función principal:

StreamReader sr = new StreamReader("c:\\Thunderbird_Inbox.txt"); 
     string working = string.Empty; 
     string mystring = string.Empty; 
     while (!sr.EndOfStream) 
     { 
      while ((mystring = sr.ReadLine()) != null) 
      { 
       if (mystring == strBeginMarker) 
       { 
        writeLog(mystring); 

        //read the next line 
        working = sr.ReadLine(); 

         while(!(working.StartsWith(strEndMarker))) 
         { 
          writeLog(working); 
          working = sr.ReadLine(); 

         } 
        } 
      } 

     } 
     this.Text = "DONE!!"; 
     sr.Close(); 

La función que escribe los mensajes seleccionados en el nuevo archivo:

public void writeLog(string sMessage) 
    { 
      fw = new System.IO.StreamWriter(path, true); 
      fw.WriteLine(sMessage); 
      fw.Flush(); 
      fw.Close(); 
    } 

Una vez más, este proceso funciona. Obtengo un buen archivo de salida, solo lleva mucho tiempo y estoy seguro de que hay formas de hacerlo más rápido.

+0

BTW: es posible que desee considerar la instrucción using en lugar de Close() manualmente; es más seguro en caso de que acierte una excepción. Mi ejemplo demuestra ... –

+1

'while (! SndEfStream)' es redundante con 'while ((mystring = sr.ReadLine())! = Null)' –

Respuesta

19

La mayor optimización sería cambiar su método writeLog para abrir el archivo una vez al comienzo de esta operación, escribir en él muchas veces, luego cerrarlo al final.

En este momento, está abriendo y cerrando el archivo cada iteración donde escribe, lo que definitivamente va a ralentizar las cosas.

intente lo siguiente:

// Open this once at the beginning! 
using(fw = new System.IO.StreamWriter(path, true)) 
{ 
    using(StreamReader sr = new StreamReader("c:\\Thunderbird_Inbox.txt")) 
    { 
     string working; 
     string mystring; 
     while ((mystring = sr.ReadLine()) != null) 
     { 
      if (mystring == strBeginMarker) 
      { 
       writeLog(mystring); 

       //read the next line 
       working = sr.ReadLine(); 

       while(!(working.StartsWith(strEndMarker))) 
       { 
        fw.WriteLine(working); 
        working = sr.ReadLine(); 
       } 
      } 
     } 
    } 
} 
this.Text = "DONE!!"; 
+0

+1 - Batirme a él. – ChaosPandion

+0

¡ESTO CAMBIA TODO! :-) Respuesta fantástica. Lo que tomaba 7 u 8 minutos terminó en aproximadamente 2 segundos. Mejor aún, aprendí algunas técnicas de codificación valiosas. – paparush

+0

@paparush: me alegro de que pudiéramos ayudar;) –

0

no tengo un archivo de texto de 150 MB para probar, pero si el servidor dispone de la memoria sería lectura lo espera en una cadena y haciendo una expresión regular tirando a cabo el trabajo de mensajes ?

+1

RegEx funcionaría, pero pueden ser bastante complejos y difíciles de "hacer bien". La solución analizadora que di es esencialmente la misma idea, pero más simple para un codificador menos experimentado. –

0

Simplemente puede declarar el objeto StreamWriter fuera del bucle while y simplemente escribir la línea dentro del bucle.

De esta manera:

StreamWriter sw = new StreamWriter(path, true); 
while 
{ 
    // ... 
    while(!(working.StartsWith(strEndMarker))) 
    { 
     sw.WriteLine(working); 
     working = sr.ReadLine(); 
    } 
} 
2

creo que deberías:

  1. Abrir archivos una vez.
  2. Carga el archivo fuente en la memoria.
  3. Rómpalo y usa varios hilos para procesar.
+1

Aunque me gusta esta respuesta, en teoría, en la práctica, probablemente no ayude demasiado. Es probable que el OP esté completamente vinculado a IO en la salida (ya que se está escribiendo en un solo archivo de salida), por lo que el multihilo probablemente rinda muy poco beneficio. No sé si valdría la pena la complejidad. –

+0

Estoy de acuerdo con Reed aquí. ¿Cómo lo dividirías en hilos? Aún necesitaría tener lógica para hacerlo. Si lo divide en trozos del mismo tamaño, tendrá que manejar el caso en el que un solo mensaje comienza en un trozo y termina en otro. Si hiciste lógica para dividirla en un límite de marcador, estás realizando un análisis previo antes de agregar la sobrecarga de subprocesamiento, probablemente haciéndolo * menos * eficiente. Eso no quiere decir que no se haya podido hacer, es mucho más trabajo del que parece merecer la pena. –

+0

@Wonko the Sane, creo que la manera más simple es primero - dividirla por partes iguales y encontrar el comienzo del primer mensaje en cada una, segundo - iniciar el proceso del hilo desde el primer mensaje de cada sección. – acoolaum

2

Haría simplemente un analizador simple. Tenga en cuenta que esto supone (como lo hace en su código anterior) que los marcadores son únicos.

Puede que tenga que jugar con el formateo de un poco de su salida, pero aquí es la idea general:

// Read the entire file and close it 
    using (StreamReader sr = new 
    StreamReader("c:\\Thunderbird_Inbox.txt");) 
    { 
     string data = sr.ReadToEnd(); 
    } 

    string newData = ""; 
    int position = data.IndexOf(strBeginMarker); 

    while (position > 0) 
    { 
     int endPosition = data.IndexOf(endMarker, position); 
     int markerLength = position + strBeginMarker.Length; 

    newData += data.Substring(markerLength, endPosition - markerLength); 

    position = data.IndexOf(strBeginMarker, position+ endStr.Length); 
    } 

    writeLog(newData); 

(Tenga en cuenta que no tengo un archivo de 150 MB para probar esto en - YMMV dependiendo de la máquina que esté usando).

+0

Al menos debe advertir a OP sobre los peligros potenciales de cargar un archivo de 150 MB de una vez a través de sr.ReadToEnd() ... –

+0

Acabo de hacerlo (de forma muy básica) –

Cuestiones relacionadas