Получите последние n строки файла с Python, подобным хвосту

Проблема заключается в том, что кто-то изменил URL-адрес, не сообщив нам, и они установили перенаправление на место. HttpClient отвечает на переадресацию, но фактически отправляет запрос в конечный пункт назначения как Get.

Мне кажется, что в HttpClient мне кажется, что он должен либо отправить окончательный запрос как сообщение , или бросить исключение, говоря, что он не может делать то, что я просил.

174
задан dbr 9 November 2009 в 21:39
поделиться

8 ответов

Код я закончил тем, что использовал. Я думаю, что это является лучшим до сих пор:

def tail(f, n, offset=None):
    """Reads a n lines from f with an offset of offset lines.  The return
    value is a tuple in the form ``(lines, has_more)`` where `has_more` is
    an indicator that is `True` if there are more lines in the file.
    """
    avg_line_length = 74
    to_read = n + (offset or 0)

    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], \
                   len(lines) > to_read or pos > 0
        avg_line_length *= 1.3
20
ответ дан Armin Ronacher 23 November 2019 в 20:31
поделиться

Это может быть более быстро, чем Ваш. Не делает предположений о длине строки. Спины через файл один блок за один раз, пока это не нашло правильное количество '\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 строк почти сразу.

кроме того, я не сжигаю много мозговых калорий, пытающихся ловко обходить выравнивание с физическими блоками ОС. Используя эти высокоуровневые пакеты ввода-вывода, я сомневаюсь, что Вы будете видеть любое последствие производительности попытки выровняться на границах блока ОС. Если Вы используете ввод-вывод низшего уровня, то Вы могли бы видеть ускорение.

119
ответ дан Mark Amery 23 November 2019 в 20:31
поделиться

Вообще-то, если задуматься это, вероятно, настолько же быстро как что-либо здесь.

def tail( f, window=20 ):
    lines= ['']*window
    count= 0
    for l in f:
        lines[count%window]= l
        count += 1
    print lines[count%window:], lines[:count%window]

Это намного более просто. И это, действительно кажется, разрывается вперед в хорошем темпе.

-1
ответ дан S.Lott 23 November 2019 в 20:31
поделиться

Для эффективности с очень большими файлами (распространенный в ситуациях с файлом журнала, где можно хотеть использовать хвост), Вы обычно не хотите читать целый файл (даже если Вы действительно делаете это, не читая целый файл в память сразу), Однако, действительно необходимо так или иначе разработать смещение в строках, а не символах. Одна возможность читает назад с, ищут () символ символом, но это очень медленно. Вместо этого лучше для обработки в больших блоках.

у меня есть служебная функция, которую я записал только что для чтения файлов назад, которые могут использоваться здесь.

import os, itertools

def rblocks(f, blocksize=4096):
    """Read file as series of blocks from end of file to start.

    The data itself is in normal order, only the order of the blocks is reversed.
    ie. "hello world" -> ["ld","wor", "lo ", "hel"]
    Note that the file must be opened in binary mode.
    """
    if 'b' not in f.mode.lower():
        raise Exception("File must be opened using binary mode.")
    size = os.stat(f.name).st_size
    fullblocks, lastblock = divmod(size, blocksize)

    # The first(end of file) block will be short, since this leaves 
    # the rest aligned on a blocksize boundary.  This may be more 
    # efficient than having the last (first in file) block be short
    f.seek(-lastblock,2)
    yield f.read(lastblock)

    for i in range(fullblocks-1,-1, -1):
        f.seek(i * blocksize)
        yield f.read(blocksize)

def tail(f, nlines):
    buf = ''
    result = []
    for block in rblocks(f):
        buf = block + buf
        lines = buf.splitlines()

        # Return all lines except the first (since may be partial)
        if lines:
            result.extend(lines[1:]) # First line may not be complete
            if(len(result) >= nlines):
                return result[-nlines:]

            buf = lines[0]

    return ([buf]+result)[-nlines:]


f=open('file_to_tail.txt','rb')
for line in tail(f, 20):
    print line

[Редактирование] Добавленная более определенная версия (избегает потребности инвертировать дважды)

1
ответ дан Brian 23 November 2019 в 20:31
поделиться

Если чтение целого файла приемлемо, тогда используют двухстороннюю очередь.

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

До 2,6, двухсторонние очереди не имели 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

, Если это - требование для чтения файла из конца, затем используйте галоп (иначе экспоненциал) поиск.

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:]
29
ответ дан A. Coady 23 November 2019 в 20:31
поделиться

Принимает подобную Unix систему на Python 2, который можно сделать:

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]
84
ответ дан Ivan Castellanos 23 November 2019 в 20:31
поделиться

Другое Решение

, если Ваш txt файл похож на это: волкодав ящерицы кошки змею мыши

Вы могли инвертировать этот файл путем простого использования индексации массива в Python '' '

contents=[]
def tail(contents,n):
    with open('file.txt') as file:
        for i in file.readlines():
            contents.append(i)

    for i in contents[:n:-1]:
        print(i)

tail(contents,-5)

результат: кошка ящерицу волка собаки

1
ответ дан 23 November 2019 в 20:31
поделиться

на основе самого популярного ответа С.Лотта (25 сентября '08, 21:43), но исправлено для небольших файлов.

def tail(the_file, lines_2find=20):  
    the_file.seek(0, 2)                         #go to end of file
    bytes_in_file = the_file.tell()             
    lines_found, total_bytes_scanned = 0, 0
    while lines_2find+1 > lines_found and bytes_in_file > total_bytes_scanned: 
        byte_block = min(1024, bytes_in_file-total_bytes_scanned)
        the_file.seek(-(byte_block+total_bytes_scanned), 2)
        total_bytes_scanned += byte_block
        lines_found += the_file.read(1024).count('\n')
    the_file.seek(-total_bytes_scanned, 2)
    line_list = list(the_file.readlines())
    return line_list[-lines_2find:]

    #we read at least 21 line breaks from the bottom, block by block for speed
    #21 to ensure we don't get a half line

Надеюсь, это будет полезно.

2
ответ дан 23 November 2019 в 20:31
поделиться
Другие вопросы по тегам:

Похожие вопросы: