Como ler arquivos gigantes linha por linha em PHP | PythonLib

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Introdução: quando o file_get_contents comum derruba o servidor

\n

Imagine a situação: você está escrevendo um parser de logs, um importador de CSV com milhões de linhas ou processando um dump de banco de dados. Você escreve $data = file_get_contents('bigfile.log'); — e seu script PHP trava com o erro Fatal error: Allowed memory size exhausted. Familiar? Se não, você teve sorte. Mas mais cedo ou mais tarde, todo desenvolvedor enfrenta o desafio: "Como ler um arquivo grande linha por linha sem derrubar o servidor?".

\n

No Stack Overflow, a pergunta "How to read a large file line by line?" acumulou centenas de milhares de visualizações. E não é por acaso. Neste artigo, vamos explorar não apenas uma, mas várias abordagens — do básico ao avançado. Você aprenderá a ler arquivos de dezenas de gigabytes usando apenas alguns megabytes de RAM. Vamos nessa!

\n\n

Por que file_get_contents e file() são ruins para arquivos grandes

\n

Antes de mergulharmos na otimização, vamos entender por que as funções padrão não são adequadas.

\n
    \n
  • file_get_contents() — carrega o arquivo inteiro em uma string. Se o arquivo tem 2 GB, o PHP tentará alocar 2 GB de RAM. Em uma hospedagem com memory_limit de 128 MB, isso é um erro fatal garantido.
  • \n
  • file() — também lê o arquivo inteiro, mas ainda o divide em um array de linhas. O consumo de memória aumenta ainda mais (mais a sobrecarga do array).
  • \n
\n

Moral da história: se o arquivo for maior que 10-20% do seu memory_limit — esqueça essas funções. Precisamos de uma abordagem baseada em fluxo.

\n\n

Método 1: O clássico — fgets() e o manipulador de arquivo

\n

A maneira mais básica e confiável é abrir o arquivo com fopen() e lê-lo linha por linha com fgets(). Essa abordagem usa o buffer interno do PHP, que geralmente é de 8 KB, e não carrega o arquivo inteiro na memória.

\n\n
<?php\n$handle = fopen('bigfile.log', 'r');\nif ($handle) {\n    while (($line = fgets($handle)) !== false) {\n        // Processa a linha\n        echo $line; // ou escreve no banco, ou conta algo\n    }\n    fclose($handle);\n} else {\n    // Erro ao abrir o arquivo\n    echo "Não foi possível abrir o arquivo.";\n}\n?>
\n\n

Vantagens: consumo mínimo de memória, funciona em qualquer versão do PHP.

\n

Desvantagens: é necessário fechar o arquivo explicitamente, não há métodos convenientes para pular cabeçalhos ou iterar com contagem de linhas.

\n

Este código suporta arquivos de qualquer tamanho, desde que você não tente armazenar todas as linhas em um array dentro do loop.

\n\n

Método 2: SplFileObject — abordagem orientada a objetos

\n

A partir do PHP 5.1, temos a excelente classe SplFileObject. Ela implementa as interfaces Iterator e SeekableIterator, tornando-a ideal para leitura linha por linha.

\n\n
<?php\n$file = new SplFileObject('bigfile.log');\nwhile (!$file->eof()) {\n    $line = $file->fgets();\n    // Processa a linha\n    echo $line;\n}\n?>
\n\n

Mas tem um detalhe! Por padrão, o SplFileObject também pode carregar linhas na memória se você usar current() dentro de um foreach. Para garantir baixo consumo, use fgets() em um loop while, como mostrado acima.

\n\n

Uma versão mais avançada — redefina as flags para otimização:

\n\n
<?php\n$file = new SplFileObject('bigfile.log');\n$file->setFlags(SplFileObject::DROP_NEW_LINE); // Remove \\n da linha\n\nforeach ($file as $lineNum => $line) {\n    // $lineNum — número da linha (começando em 0)\n    echo "Linha $lineNum: $line\\n";\n}\n?>
\n\n

Importante: Ao usar foreach com SplFileObject, o PHP ainda lê uma linha por vez. A memória não é afetada. Mas se você tentar chamar $file->seek(1000000) e pular pelo arquivo — cuidado: o seek percorre desde o início até a linha desejada, o que pode ser lento para arquivos enormes.

Blogs

Book Recommendations