Acabei de descobrir que meu script apresenta um erro fatal:
Fatal error: Allowed memory size of 268435456 bytes exhausted (tried to allocate 440 bytes) in C:\process_txt.php on line 109
Essa linha é esta:
$lines = count(file($path)) - 1;
Então eu acho que está tendo dificuldade para carregar o arquivo na memória e contar o número de linhas, existe uma maneira mais eficiente de fazer isso sem ter problemas de memória?
Os arquivos de texto de que preciso para contar o número de linhas variam de 2 MB a 500 MB. Talvez um show às vezes.
Obrigado a todos por qualquer ajuda.
\n
) sendo analisado em uma máquina Windows (PHP_EOL == '\r\n'
)fgets($handle, 1);
?substr_count()
, mas se você tiver linhas muito longas, você precisa chamarwhile()
efgets()
muito mais causando uma desvantagem. Não se esqueça:fgets()
não lê linha por linha. Ele lê apenas a quantidade de caracteres que você definiu$length
e se contiver uma quebra de linha, ele interrompe o$length
que foi definido.while(!feof())
fará com que você leia uma linha extra, porque o indicador EOF não é definido até que você tente ler no final do arquivo.$line = fgets($handle);
poderia ser apenasfgets($handle);
porque$line
nunca é usado.Usar um loop de
fgets()
chamadas é a solução perfeita e a mais direta de escrever, no entanto:mesmo que internamente o arquivo seja lido usando um buffer de 8.192 bytes, seu código ainda precisa chamar essa função para cada linha.
é tecnicamente possível que uma única linha seja maior do que a memória disponível se você estiver lendo um arquivo binário.
Este código lê um arquivo em blocos de 8kB cada e conta o número de novas linhas dentro desse bloco.
Se o comprimento médio de cada linha for no máximo 4kB, você já começará a economizar nas chamadas de função, que podem aumentar ao processar arquivos grandes.
Benchmark
Fiz um teste com um arquivo de 1 GB; aqui estão os resultados:
O tempo é medido em segundos em tempo real, veja aqui o que significa real
fonte
Solução de objeto orientado simples
Atualizar
Outra maneira de fazer isso é com
PHP_INT_MAX
noSplFileObject::seek
método.fonte
wc -l
(por causa da bifurcação, suponho), especialmente em arquivos pequenos.Se você estiver executando em um host Linux / Unix, a solução mais fácil seria usar
exec()
ou semelhante para executar o comandowc -l $path
. Apenas certifique-se de limpar$path
primeiro para ter certeza de que não é algo como "/ caminho / para / arquivo; rm -rf /".fonte
Há uma maneira mais rápida que descobri que não requer o loop por todo o arquivo
apenas em sistemas * nix , pode haver uma maneira semelhante no Windows ...
fonte
exec('wc -l '.escapeshellarg($file).' 2>/dev/null')
Se estiver usando PHP 5.5, você pode usar um gerador . Porém, isso NÃO funcionará em nenhuma versão do PHP anterior à 5.5. De php.net:
"Os geradores fornecem uma maneira fácil de implementar iteradores simples sem a sobrecarga ou complexidade de implementar uma classe que implemente a interface do Iterator."
fonte
try
/finally
não é estritamente necessário, o PHP fechará automaticamente o arquivo para você. Você provavelmente também deve mencionar que a contagem real pode ser feita usandoiterator_count(getFiles($file))
:)Esta é uma adição à solução de Wallace de Souza
Ele também pula linhas vazias durante a contagem:
fonte
Se você estiver no Linux, pode simplesmente fazer:
Você só precisa encontrar o comando certo se estiver usando outro sistema operacional
Saudações
fonte
Eu queria adicionar um pequeno conserto à função acima ...
em um exemplo específico onde eu tinha um arquivo contendo a palavra 'teste', a função retornou 2 como resultado. então eu precisava adicionar uma verificação se fgets retornou falso ou não :)
diverta-se :)
fonte
A contagem do número de linhas pode ser feita pelos seguintes códigos:
fonte
Você tem várias opções. A primeira é aumentar a memória disponível permitida, o que provavelmente não é a melhor maneira de fazer as coisas, visto que você afirma que o arquivo pode ficar muito grande. A outra maneira é usar fgets para ler o arquivo linha por linha e incrementar um contador, o que não deve causar nenhum problema de memória, já que apenas a linha atual está na memória a qualquer momento.
fonte
Há outra resposta que achei que poderia ser uma boa adição a esta lista.
Se você
perl
instalou e é capaz de executar coisas do shell em PHP:Isso deve lidar com a maioria das quebras de linha, sejam de arquivos criados pelo Unix ou pelo Windows.
DOIS desvantagens (pelo menos):
1) Não é uma boa ideia ter seu script tão dependente do sistema em que está sendo executado (pode não ser seguro assumir que Perl e wc estão disponíveis)
2) Apenas um pequeno erro ao escapar e você entregou o acesso a um shell em sua máquina.
Como acontece com a maioria das coisas que sei (ou acho que sei) sobre codificação, obtive essas informações de outro lugar:
Artigo de John Reeve
fonte
fonte
Com base na solução de Dominic Rodger, aqui está o que eu uso (ele usa wc se disponível, caso contrário, retorna para a solução de Dominic Rodger).
https://github.com/lingtalfi/Bat/blob/master/FileTool.php
fonte
Eu uso este método para simplesmente contar quantas linhas em um arquivo. Qual é a desvantagem de fazer estes versículos as outras respostas. Estou vendo muitas linhas em oposição à minha solução de duas linhas. Acho que há um motivo pelo qual ninguém faz isso.
fonte
A solução de plataforma cruzada mais sucinta que armazena apenas uma linha de cada vez.
Infelizmente, temos que definir o
READ_AHEAD
sinalizador, caso contrário,iterator_count
bloqueia indefinidamente. Caso contrário, isso seria uma linha única.fonte
Para apenas contar as linhas, use:
fonte