Estou escrevendo um visualizador de arquivo de log para um aplicativo Web e para isso quero paginar pelas linhas do arquivo de log. Os itens no arquivo são baseados em linhas com o item mais recente na parte inferior.
Então, eu preciso de um tail()
método que possa ler n
linhas da parte inferior e suporte um deslocamento. O que eu criei se parece com isso:
def tail(f, n, offset=0):
"""Reads a n lines from f with an offset of offset lines."""
avg_line_length = 74
to_read = n + offset
while 1:
try:
f.seek(-(avg_line_length * to_read), 2)
except IOError:
# woops. apparently file is smaller than what we want
# to step back, go to the beginning instead
f.seek(0)
pos = f.tell()
lines = f.read().splitlines()
if len(lines) >= to_read or pos == 0:
return lines[-to_read:offset and -offset or None]
avg_line_length *= 1.3
Essa é uma abordagem razoável? Qual é a maneira recomendada de ajustar os arquivos de log com as compensações?
seek(0,2)
entãotell()
) e use esse valor para procurar em relação ao início.open
comando usado para gerar of
objeto de arquivo deve ser especificado, porque dependendo sef=open(..., 'rb')
ouf=open(..., 'rt')
of
devem ser processadas de forma diferenteRespostas:
Isso pode ser mais rápido que o seu. Não faz suposições sobre o comprimento da linha. Faz o backup do arquivo um bloco de cada vez, até encontrar o número certo de caracteres '\ n'.
Não gosto de suposições complicadas sobre o comprimento da linha quando - por uma questão prática - você nunca pode saber coisas assim.
Geralmente, isso localizará as últimas 20 linhas na primeira ou na segunda passagem pelo loop. Se sua coisa de 74 caracteres é realmente precisa, você cria o tamanho do bloco 2048 e segue 20 linhas quase imediatamente.
Além disso, eu não queimo muitas calorias do cérebro tentando refinar o alinhamento com os blocos físicos do sistema operacional. Usando esses pacotes de E / S de alto nível, duvido que você veja qualquer consequência de desempenho ao tentar alinhar nos limites do bloco do SO. Se você usar E / S de nível inferior, poderá ver uma aceleração.
ATUALIZAR
para Python 3.2 e superior, siga o processo em bytes, como nos arquivos de texto (aqueles abertos sem um "b" na string de modo), apenas buscas relativas ao início do arquivo são permitidas (a exceção é procurar até o final do arquivo) com busca (0, 2)) .:
por exemplo:
f = open('C:/.../../apache_logs.txt', 'rb')
fonte
io.UnsupportedOperation: can't do nonzero end-relative seeks
alterar o deslocamento para 0, mas isso anula o objetivo da função.Assume um sistema unix no Python 2 que você pode fazer:
Para python 3, você pode fazer:
fonte
offset_total = str(n+offset)
e substituir esta linhastdin,stdout = os.popen2("tail -n "+offset_total+" "+f)
para evitarTypeErrors (cannot concatenate int+str)
Aqui está a minha resposta. Pitão puro. Usando o timeit, parece bem rápido. Seguindo 100 linhas de um arquivo de log que possui 100.000 linhas:
Aqui está o código:
fonte
if len(lines_found) > lines:
realmente necessário? Aloop
condição não a pegaria também?os.SEEK_END
usada simplesmente para maior clareza? Tanto quanto eu descobri, seu valor é constante (= 2). Eu estava pensando em deixá-lo de fora para poder deixar de fora oimport os
. Obrigado pela ótima solução!os.SEEK_END
por seu equivalente inteiro. Estava principalmente lá para facilitar a leitura.while len(lines_found) < lines
parawhile len(lines_found) <= lines
na minha cópia. Obrigado!Se a leitura do arquivo inteiro for aceitável, use um deque.
Antes do 2.6, o deques não tinha uma opção maxlen, mas é fácil de implementar.
Se for necessário ler o arquivo a partir do final, use uma pesquisa a galope (também conhecida como exponencial).
fonte
pos *= 2
parece completamente arbitrário. Qual é o seu significado?A resposta de S.Lott acima quase funciona para mim, mas acaba me dando linhas parciais. Acontece que ele corrompe os dados nos limites do bloco, porque os dados retêm os blocos de leitura na ordem inversa. Quando '' .join (data) é chamado, os blocos estão na ordem errada. Isso corrige isso.
fonte
O código que acabei usando. Eu acho que este é o melhor até agora:
fonte
Solução simples e rápida com mmap:
fonte
.rfind
método para procurar novas linhas para trás em vez de executar verificações de byte por vez no nível Python; no CPython, substituindo o código no nível Python por As chamadas internas C geralmente ganham muito). Para entradas menores, odeque
with amaxlen
é mais simples e provavelmente similarmente rápido.Uma versão compatível com python3 ainda mais limpa que não insere, mas acrescenta e reverte:
use-o assim:
fonte
Atualize a solução @papercrane para python3. Abra o arquivo com
open(filename, 'rb')
e:fonte
Postando uma resposta a pedido dos comentaristas sobre a minha resposta a uma pergunta semelhante em que a mesma técnica foi usada para alterar a última linha de um arquivo, não apenas obtê-la.
Para um arquivo de tamanho significativo,
mmap
é a melhor maneira de fazer isso. Para melhorar ammap
resposta existente , esta versão é portátil entre Windows e Linux e deve ser executada mais rapidamente (embora não funcione sem algumas modificações no Python de 32 bits com arquivos na faixa de GB, consulte a outra resposta para obter dicas sobre como lidar com isso. e para modificar para trabalhar no Python 2 ).Isso pressupõe que o número de linhas atadas seja pequeno o suficiente para que você possa lê-las na memória com segurança de uma só vez; você também pode fazer disso uma função de gerador e ler manualmente uma linha de cada vez, substituindo a linha final por:
Por fim, isso é lido no modo binário (necessário usar
mmap
) parastr
fornecer linhas (Py2) ebytes
linhas (Py3); se você quiserunicode
(Py2) oustr
(Py3), a abordagem iterativa pode ser aprimorada para decodificar e / ou corrigir novas linhas:Nota: digitei tudo isso em uma máquina na qual não tenho acesso ao Python para testar. Por favor, deixe-me saber se eu digitei alguma coisa; isso foi semelhante o suficiente à minha outra resposta e acho que deve funcionar, mas os ajustes (por exemplo, lidar com um
offset
) podem levar a erros sutis. Por favor, deixe-me saber nos comentários se houver algum erro.fonte
Eu achei o Popen acima a melhor solução. É rápido e sujo e funciona Para o python 2.6 na máquina Unix, usei o seguinte
A saída de saída conterá as últimas n linhas do código. para iterar através da saída de linha por linha, faça:
fonte
com base na resposta mais votada de S.Lott (25 de setembro de 2008 às 21:43), mas corrigida para arquivos pequenos.
Espero que isso seja útil.
fonte
Existem algumas implementações existentes do tail on pypi que você pode instalar usando o pip:
Dependendo da sua situação, pode haver vantagens em usar uma dessas ferramentas existentes.
fonte
tailhead
,tailer
mas eles não funcionaram. Também tenteimtFileUtil
. Ele estava lançando um erro inicialmente porque asprint
instruções não estavam entre parênteses (estou no Python 3.6). Eu os adicioneireverse.py
e as mensagens de erro desapareceram, mas quando meu script chama o module (mtFileUtil.tail(open(logfile_path), 5)
), ele não imprime nada.Simples:
fonte
Para obter eficiência com arquivos muito grandes (comum em situações de arquivo de log em que você pode usar o tail), geralmente você deseja evitar a leitura do arquivo inteiro (mesmo que faça isso sem ler o arquivo inteiro na memória de uma só vez). precisa de alguma forma calcular o deslocamento em linhas, em vez de caracteres. Uma possibilidade é ler de trás para frente com seek () char por char, mas isso é muito lento. Em vez disso, é melhor processar em blocos maiores.
Eu tenho uma função utilitária que escrevi há um tempo atrás para ler arquivos que podem ser usados aqui atrás.
[Editar] Adicionado versão mais específica (evita a necessidade de reverter duas vezes)
fonte
você pode ir para o final do seu arquivo com f.seek (0, 2) e depois ler as linhas uma a uma com a seguinte substituição para readline ():
fonte
Com base na resposta Eyecue (10 de junho de 2010 às 21:28): esta classe adiciona o método head () e tail () ao arquivo do objeto.
Uso:
fonte
Várias dessas soluções têm problemas se o arquivo não terminar em \ n ou em garantir a leitura da primeira linha completa.
fonte
Aqui está uma implementação bastante simples:
fonte
f.seek
? Por que não antes dowith open
? Além disso, por queexcept
você faz umf.readlines()
??Há um módulo muito útil que pode fazer isso:
fonte
Outra solução
se o seu arquivo txt estiver assim: rato cobra gato lagarto lobo cachorro
você pode reverter esse arquivo simplesmente usando a indexação de array em python '' '
resultado: cachorro lagarto lobo gato
fonte
A maneira mais simples é usar
deque
:fonte
Eu tive que ler um valor específico da última linha de um arquivo e me deparei com esse tópico. Em vez de reinventar a roda no Python, acabei com um pequeno script de shell, salvo como / usr / local / bin / get_last_netp:
E no programa Python:
fonte
Não é o primeiro exemplo usando um deque, mas um exemplo mais simples. Este é geral: funciona em qualquer objeto iterável, não apenas em um arquivo.
fonte
fonte
fonte
fonte
fonte
Atualização para resposta dada por A.Coady
Funciona com python 3 .
Isso usa a Pesquisa Exponencial e armazenará em buffer apenas as
N
linhas de trás e é muito eficiente.fonte
Pensando bem, isso provavelmente é tão rápido quanto qualquer coisa aqui.
É muito mais simples. E parece rasgar a um bom ritmo.
fonte