86 votos

Leer archivos de texto grandes arroyos en C#

Tengo la hermosa tarea de trabajar en cómo manejar grandes archivos que se cargan en la aplicación del editor de secuencias de comandos (como VBA para nuestro producto interno rápida de macros). La mayoría de los archivos son alrededor de 300-400Kb que está muy bien de carga. Pero cuando van más allá de 100Mb el proceso tiene un tiempo duro como era de esperar.

Lo que pasa es que el archivo se lee y se metió en un control RichTextBox que luego de navegar - no se preocupe demasiado acerca de esta parte.

El programador que escribió el código inicial es simplemente el uso de un StreamReader y haciendo

[Reader].ReadToEnd() 

que podría tomar bastante tiempo para completar.

Mi tarea es la de romper con este trozo de código, leer en trozos en un buffer y se mostrará una barra de progreso con una opción para cancelar.

Algunas hipótesis:

  • La mayoría de los archivos será de 30-40Mb
  • El contenido de el archivo es de texto (no binario), algunos están en formato UNIX, algunas son DOS.
  • Una vez que el contenido se recuperan nos entrenamiento lo que terminator se utiliza.
  • No queridos de que se trate una vez cargado el tiempo que se tarda en procesar en el control richtextbox, solo la carga inicial del texto.

Ahora las preguntas:

  • Puedo simplemente usar StreamReader, a continuación, compruebe la Longitud de la propiedad (por lo ProgressMax) y emitir una Lectura de un conjunto de tamaño de búfer de e iterar en un bucle while , MIENTRAS que en el interior de un trabajo en segundo plano para que no se bloque la principal subproceso de interfaz de usuario? A continuación, devolver el stringbuilder para el hilo principal una vez que se ha completado.
  • El contenido se va a un StringBuilder, puedo inicializar el SB con el tamaño de la corriente si la longitud está disponible?

Son estos (en sus opiniones profesionales) de las buenas ideas? He tenido algunos problemas en el pasado con la lectura del contenido de los Arroyos, porque siempre se perderá el último par de bytes o algo, pero voy a hacer otra pregunta si este es el caso.

164voto

Eric J. Puntos 73338

Soy muy tarde a la fiesta, pero sorprende que a nadie se menciona que puede mejorar la velocidad de lectura mediante el uso de un BufferedStream, como este:

using (FileStream fs = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
using (BufferedStream bs = new BufferedStream(fs))
using (StreamReader sr = new StreamReader(bs))
{
    string line;
    while ((line = sr.ReadLine()) != null)
    {

    }
}

De marzo de 2013 ACTUALIZACIÓN

Hace poco me escribió el código para la lectura y el procesamiento (búsqueda de texto) 1 GB-ish archivos de texto (mucho más grandes que los archivos involucrados aquí) y ha logrado un significativo aumento del rendimiento mediante el uso de un productor/consumidor patrón. El productor de la tarea de lectura en las líneas de texto usando el BufferedStream y se los entregó a un independiente consumidor tarea en la que se hizo la búsqueda.

He usado esto como una oportunidad para aprender TPL de flujo de datos, que es muy adecuado para la rápida codificación de este patrón.

Por qué BufferedStream es más rápido

Un buffer es un bloque de bytes en la memoria se utiliza para almacenar en caché los datos, reduciendo así el número de llamadas al sistema operativo. Los búferes de mejorar el rendimiento de lectura y escritura. Un búfer puede ser utilizado ya sea para lectura o escritura, pero nunca ambos a la vez. Los métodos de Lectura y Escritura de BufferedStream mantener automáticamente el búfer.

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

14voto

Christian Hayter Puntos 17999

Usted dice que usted ha solicitado para mostrar una barra de progreso mientras que un archivo de gran tamaño se está cargando. Es que debido a que los usuarios realmente quieren ver el exacto % de la carga de archivos, o simplemente porque quieren retroalimentación visual de que algo está sucediendo?

Si esto último es cierto, entonces la solución se vuelve mucho más sencillo. Just do reader.ReadToEnd() en un subproceso en segundo plano, y mostrar un recuadro de tipo barra de progreso en lugar de uno.

Menciono este punto porque en mi experiencia esto es a menudo el caso. Cuando usted está escribiendo un procesamiento de datos de programa, los usuarios que definitivamente va a estar interesado en un % completa la figura, pero para los simples pero lentas las actualizaciones de IU, que son más propensos solo quiero saber de que el equipo no ha fallado. :-)

6voto

Tufo Puntos 258

utilizar un fondo trabajador y leer sólo un número limitado de líneas, leer más, sólo cuando el usuario se desplaza

y tratar de no usar nunca ReadToEnd(), es una de las funciones que usted piensa "¿por qué hacen esto?", su una secuencia de Comandos-Kidies-el Ayudante que va bien con cosas pequeñas, pero como se puede ver, es sux para archivos de gran tamaño...

EDITAR
los chicos diciendo que el uso de StringBuilder es necesario leer el MSDN más a menudo:

Consideraciones De Rendimiento
La Concat y AppendFormat métodos concatenar nuevos datos en una Cadena o un objeto StringBuilder. Un objeto de Cadena de la operación de concatenación de siempre crea un nuevo objeto a partir de la cadena existente y los nuevos datos. Un objeto StringBuilder mantiene un buffer para dar cabida a la concatenación de los nuevos datos. Los nuevos datos se anexa al final del búfer si la habitación está disponible; de lo contrario, una nueva, más grande búfer asignado, los datos del búfer original se copia en el nuevo búfer, los nuevos datos se anexa a la nueva búfer. El rendimiento de una operación de concatenación de una Cadena o un objeto StringBuilder depende de la frecuencia de una asignación de memoria se produce.
Una operación de concatenación de cadenas siempre asigna la memoria, mientras que un StringBuilder la operación de concatenación sólo asigna memoria si el objeto StringBuilder búfer es demasiado pequeño para dar cabida a los nuevos datos. En consecuencia, la clase String es preferible para una operación de concatenación si un número fijo de objetos String se concatenan. En ese caso, el individuo de la concatenación de operaciones, incluso podría ser combinadas en una sola operación por el compilador. Un objeto StringBuilder es preferible para una operación de concatenación si un número arbitrario de las cadenas concatenadas; por ejemplo, si un bucle concatena un número aleatorio de las cadenas de la entrada del usuario.


Que significa enorme asignación de memoria, lo que se convierte en un gran uso de los archivos de intercambio del sistema, que simula las secciones de su disco duro, para actuar como la memoria RAM, pero el disco duro es muy lento. El StringBuilder opción parece bien para los que utilizan el sistema como un mono-usuario, pero cuando se tienen 2 o más usuarios la lectura de archivos de gran tamaño al mismo tiempo, usted tiene un problema.

5voto

ChaosPandion Puntos 37025

Esto debería ser suficiente para empezar.

class Program
{        
    static void Main(String[] args)
    {
        const int bufferSize = 1024;

        var sb = new StringBuilder();
        var buffer = new Char[bufferSize];
        var length = 0L;
        var totalRead = 0L;
        var count = bufferSize; 

        using (var sr = new StreamReader(@"C:\Temp\file.txt"))
        {
            length = sr.BaseStream.Length;               
            while (count > 0)
            {                    
                count = sr.Read(buffer, 0, bufferSize);
                sb.Append(buffer, 0, count);
                totalRead += count;
            }                
        }

        Console.ReadKey();
    }
}

4voto

James Puntos 40024

Echa un vistazo en el siguiente fragmento de código. Usted ha mencionado Most files will be 30-40mb , esto pretende leer 180mb en 1,4 segundos en un Intel Quad Core:

private int _bufferSize = 16384; 

private void ReadFile(string filename) 
{
    StringBuilder stringBuilder = new StringBuilder();     
    FileStream fileStream = new FileStream(filename, FileMode.Open, FileAccess.Read);  

    using (StreamReader streamReader = new StreamReader(fileStream))     
    {        
        char[] fileContents = new char[_bufferSize];         
        int charsRead = streamReader.Read(fileContents, 0, _bufferSize); 

        // Can't do much with 0 bytes        
        if (charsRead == 0)             
            throw new Exception("File is 0 bytes"); 

        while (charsRead > 0)         
        {             
            stringBuilder.Append(fileContents);             
            charsRead = streamReader.Read(fileContents, 0, _bufferSize); 
        }     
    } 
}

Artículo original

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X