获取文件的最后n行(Python中类似tai的功能)

2024-03-28 22:26:43 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在为一个web应用程序编写一个日志文件查看器,为此我想对日志文件的行进行分页。文件中的项是基于最新项的行。

所以我需要一个tail()方法,它可以从底部读取n行并支持偏移量。我想到的是这样的:

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

这样做合理吗?使用偏移量跟踪日志文件的建议方法是什么?


Tags: or文件to方法poswebreadline
3条回答

这可能比你的快。对线的长度没有任何假设。一次返回一个块,直到找到正确数量的'\n'字符。

def tail( f, lines=20 ):
    total_lines_wanted = lines

    BLOCK_SIZE = 1024
    f.seek(0, 2)
    block_end_byte = f.tell()
    lines_to_go = total_lines_wanted
    block_number = -1
    blocks = [] # blocks of size BLOCK_SIZE, in reverse order starting
                # from the end of the file
    while lines_to_go > 0 and block_end_byte > 0:
        if (block_end_byte - BLOCK_SIZE > 0):
            # read the last block we haven't yet read
            f.seek(block_number*BLOCK_SIZE, 2)
            blocks.append(f.read(BLOCK_SIZE))
        else:
            # file too small, start from begining
            f.seek(0,0)
            # only read what was not read
            blocks.append(f.read(block_end_byte))
        lines_found = blocks[-1].count('\n')
        lines_to_go -= lines_found
        block_end_byte -= BLOCK_SIZE
        block_number -= 1
    all_read_text = ''.join(reversed(blocks))
    return '\n'.join(all_read_text.splitlines()[-total_lines_wanted:])

我不喜欢在实际情况下,当你永远不可能知道这样的事情的时候,对线的长度做一些复杂的假设。

通常,这将在第一次或第二次通过循环时定位最后20行。如果你的74个字符的东西是准确的,你使块大小2048,你将尾随20行几乎立即。

而且,我也不会消耗大量的大脑卡路里来巧妙地调整物理操作系统块。使用这些高级I/O包,我怀疑您会看到试图在OS块边界上对齐的任何性能结果。如果使用较低级别的I/O,则可能会看到加速。

如果可以读取整个文件,则使用deque。

from collections import deque
deque(f, maxlen=n)

在2.6之前,deques没有maxlen选项,但是它很容易实现。

import itertools
def maxque(items, size):
    items = iter(items)
    q = deque(itertools.islice(items, size))
    for item in items:
        del q[0]
        q.append(item)
    return q

如果需要从末尾读取文件,则使用gallop(也称为指数)搜索。

def tail(f, n):
    assert n >= 0
    pos, lines = n+1, []
    while len(lines) <= n:
        try:
            f.seek(-pos, 2)
        except IOError:
            f.seek(0)
            break
        finally:
            lines = list(f)
        pos *= 2
    return lines[-n:]

假设Python 2上有一个类似unix的系统,您可以执行以下操作:

import os
def tail(f, n, offset=0):
  stdin,stdout = os.popen2("tail -n "+n+offset+" "+f)
  stdin.close()
  lines = stdout.readlines(); stdout.close()
  return lines[:,-offset]

对于python 3,您可以执行以下操作:

import subprocess
def tail(f, n, offset=0):
    proc = subprocess.Popen(['tail', '-n', n + offset, f], stdout=subprocess.PIPE)
    lines = proc.stdout.readlines()
    return lines[:, -offset]

相关问题 更多 >