none
[Resolvido] - Importando arquivos com streamReader.ReadBlock (buffer) RRS feed

  • Pergunta

  • Olá amigos!

    Precisava importar uma série de grandes arquivos de texto e por encontrar pouco material de pesquisa, especificamente para o meu problema, decidi publicar a solução aqui. Acredito que ajudará mais alguém.

    Meus arquivos são de 3.000.000 de registros p/ cima. Tentei ler linha-a-linha, com streamReader.ReadLine(), porém era inviável. Por outro lado, os arquivos são grandes demais para carrega-los na memória.

    A solução foi carregar os arquivos na memória em blocos (buffers), utilizando o streamReader.ReadBlock().

    A dificuldade que tive foi que o ReadBlock() lê byte-a-byte, ocorrendo de uma linha ou outra ficar pela metade. Aí no próximo buffer a primeira linha vinha incompleta. Para corrigir, alimento um string (resto) e concateno com a 1º linha (primeiraLinha) do próximo buffer.

    Outro detalhe importante, na utilização do Split, na maioria dos exemplos a 1º verificação das variáveis são acompanhadas do Trim(), para eliminar os espaços. Neste caso não utilizo, pois concatenava a 1º e 2º linha do buffer. 

    using System;
    using System.IO;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace ConsoleApplication2
    {
        class Program
        {
            static void Main()
            {
                const string arquivo = "Arquivo1.txt";
                using (var streamReader = new StreamReader(arquivo))
                {
                    int deslocamento = 1000;
                    int pStart = 0; // posição inicial do buffer
                    int pEnd = deslocamento; // posição final do buffer
                    string resto = ""; 
                    for (int i = pStart; i < int.MaxValue; i += pStart)
                    {
                        string primeiraLinha;
                        char[] buffer = new char[pEnd-pStart];
                        streamReader.ReadBlock(buffer, 0, buffer.Length);
                        var bufferString = new String(buffer);
                        string[] bufferSplit = null;
                        bufferSplit = bufferString.Split(new char[] { '\n' });
                        foreach (var bs in bufferSplit )
                        {
                            if (bs != "")
                            {
                                if (resto != "")
                                {
                                    primeiraLinha = resto + bs;
                                    Console.WriteLine(primeiraLinha);
                                    resto = "";
                                }
                                else
                                {
                                    if (bs.Contains('\r'))
                                    {
                                        Console.WriteLine(bs);
                                    }
                                    else
                                    {
                                        resto = bs;
                                    }
                                }
                            }
                        }
                        Console.ReadLine();
                        // Desloca os ponteiros
                        pStart = pEnd;
                        pEnd += deslocamento;
                        if (bufferString == null)
                            break;
                    }
                }
            }
        }
    }

    Tive uma grande ajuda do meu colega de treinamento, Gabriel Gustaf, na resolução deste problema.

    Se alguém tiver alguma sugestão para melhorar ainda mais a performance, ou alguma observação a fazer, fique à vontade.


    segunda-feira, 15 de abril de 2013 15:42