Как я печатаю последние строки «n» (int num) в текстовом файле? [Дубликат]

DELETE FROM blob 
WHERE fileid NOT IN 
       (SELECT id 
        FROM files 
        WHERE id is NOT NULL/*This line is unlikely to be needed 
                               but using NOT IN...*/
      )
152
задан dbr 9 November 2009 в 22:39
поделиться

28 ответов

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

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
19
ответ дан Armin Ronacher 21 August 2018 в 08:55
поделиться

Если чтение всего файла приемлемо, используйте 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

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

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:]
27
ответ дан A. Coady 21 August 2018 в 08:55
поделиться
  • 1
    Почему эта нижняя функция работает? pos *= 2 кажется совершенно произвольным. Каково его значение? – 2mac 29 December 2014 в 20:06
  • 2
    @ 2mac Экспоненциальный поиск . Он читает с конца файла итеративно, удваивая количество, считанное каждый раз, до тех пор, пока не будет найдено достаточно строк. – A. Coady 17 March 2015 в 00:08

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

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

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

[Edit ] Добавлена ​​более конкретная версия (избегает необходимости обратного обращения дважды)

1
ответ дан Brian 21 August 2018 в 08:55
поделиться
  • 1
    Быстрые тесты показывают, что это намного хуже, чем моя версия сверху. Вероятно, из-за вашей буферизации. – Armin Ronacher 25 September 2008 в 23:00
  • 2
    Я подозреваю, что это потому, что я делаю несколько обращений назад, поэтому не так хорошо использую буфер readahead. Тем не менее, я думаю, что это может сделать лучше, когда ваше предположение о длине строки неточно (например, очень большие строки), поскольку это позволяет избежать перечитывания данных в этом случае. – Brian 25 September 2008 в 23:23

Некоторые из этих решений имеют проблемы, если файл не заканчивается в\n или в обеспечении чтения первой первой строки.

def tail(file, n=1, bs=1024):
    f = open(file)
    f.seek(-1,2)
    l = 1-f.read(1).count('\n') # If file doesn't end in \n, count it anyway.
    B = f.tell()
    while n >= l and B > 0:
            block = min(bs, B)
            B -= block
            f.seek(B, 0)
            l += f.read(block).count('\n')
    f.seek(B, 0)
    l = min(l,n) # discard first (incomplete) line if l > n
    lines = f.readlines()[-l:]
    f.close()
    return lines
1
ответ дан David Rogers 21 August 2018 в 08:55
поделиться

Простое и быстрое решение с mmap:

import mmap
import os

def tail(filename, n):
    """Returns last n lines from the filename. No exception handling"""
    size = os.path.getsize(filename)
    with open(filename, "rb") as f:
        # for Windows the mmap parameters are different
        fm = mmap.mmap(f.fileno(), 0, mmap.MAP_SHARED, mmap.PROT_READ)
        try:
            for i in xrange(size - 1, -1, -1):
                if fm[i] == '\n':
                    n -= 1
                    if n == -1:
                        break
            return fm[i + 1 if i else 0:].splitlines()
        finally:
            fm.close()
13
ответ дан dimitri 21 August 2018 в 08:55
поделиться
  • 1
    Вероятно, это самый быстрый ответ, когда вход может быть огромным (или это было бы, если бы он использовал метод .rfind для сканирования назад для новых строк, вместо того, чтобы выполнять байты во время проверки на уровне Python, в CPython, заменяя Python код уровня с встроенными вызовами C обычно выигрывает много). Для более мелких входов deque с maxlen проще и, вероятно, так же быстро. – ShadowRanger 19 November 2015 в 19:41

Обновить решение @papercrane для python3. Откройте файл с помощью open(filename, 'rb') и:

def tail(f, window=20):
    """Returns the last `window` lines of file `f` as a list.
    """
    if window == 0:
        return []

    BUFSIZ = 1024
    f.seek(0, 2)
    remaining_bytes = f.tell()
    size = window + 1
    block = -1
    data = []

    while size > 0 and remaining_bytes > 0:
        if remaining_bytes - BUFSIZ > 0:
            # Seek back one whole BUFSIZ
            f.seek(block * BUFSIZ, 2)
            # read BUFFER
            bunch = f.read(BUFSIZ)
        else:
            # file too small, start from beginning
            f.seek(0, 0)
            # only read what was not read
            bunch = f.read(remaining_bytes)

        bunch = bunch.decode('utf-8')
        data.insert(0, bunch)
        size -= bunch.count('\n')
        remaining_bytes -= BUFSIZ
        block -= 1

    return ''.join(data).splitlines()[-window:]
1
ответ дан Emilio 21 August 2018 в 08:55
поделиться

, основанный на верхнем голосовом ответе С.Лотта (сент. 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
ответ дан Eyecue 21 August 2018 в 08:55
поделиться

На основании ответа на Eyecue (10 июн 2010 в 21:28): этот класс добавляет метод head () и tail () в файл-объект.

class File(file):
    def head(self, lines_2find=1):
        self.seek(0)                            #Rewind file
        return [self.next() for x in xrange(lines_2find)]

    def tail(self, lines_2find=1):  
        self.seek(0, 2)                         #go to end of file
        bytes_in_file = self.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)
            self.seek(-(byte_block+total_bytes_scanned), 2)
            total_bytes_scanned += byte_block
            lines_found += self.read(1024).count('\n')
        self.seek(-total_bytes_scanned, 2)
        line_list = list(self.readlines())
        return line_list[-lines_2find:]

Использование:

f = File('path/to/file', 'r')
f.head(3)
f.tail(3)
1
ответ дан fdb 21 August 2018 в 08:55
поделиться

Ответ С.Лотта выше почти работает для меня, но в конечном итоге дает мне частичные линии. Оказывается, он искажает данные на границах блоков, поскольку данные содержат блоки чтения в обратном порядке. Когда вызывается '.join (data), блоки находятся в неправильном порядке. Это исправляет это.

def tail(f, window=20):
    """
    Returns the last `window` lines of file `f` as a list.
    """
    if window == 0:
        return []
    BUFSIZ = 1024
    f.seek(0, 2)
    bytes = f.tell()
    size = window + 1
    block = -1
    data = []
    while size > 0 and bytes > 0:
        if bytes - BUFSIZ > 0:
            # Seek back one whole BUFSIZ
            f.seek(block * BUFSIZ, 2)
            # read BUFFER
            data.insert(0, f.read(BUFSIZ))
        else:
            # file too small, start from begining
            f.seek(0,0)
            # only read what was not read
            data.insert(0, f.read(bytes))
        linesFound = data[0].count('\n')
        size -= linesFound
        bytes -= BUFSIZ
        block -= 1
    return ''.join(data).splitlines()[-window:]
25
ответ дан Francisco Vieira 21 August 2018 в 08:55
поделиться
  • 1
    Вставка в начале списка - плохая идея. Почему бы не использовать структуру deque? – Sergey11g 29 September 2017 в 08:26

Вот довольно простая реализация:

with open('/etc/passwd', 'r') as f:
  try:
    f.seek(0,2)
    s = ''
    while s.count('\n') < 11:
      cur = f.tell()
      f.seek((cur - 10))
      s = f.read(10) + s
      f.seek((cur - 10))
    print s
  except Exception as e:
    f.readlines()
2
ответ дан GL2014 21 August 2018 в 08:55
поделиться
  • 1
    Отличный пример! Не могли бы вы объяснить использование try перед f.seek? Почему не раньше with open? Кроме того, почему в except вы делаете f.readlines() ?? – Karim 7 July 2017 в 15:33
  • 2
    Честно говоря, попытка должна, вероятно, идти первой. Я не помню, чтобы иметь причины не заразиться open (), кроме здоровой стандартной системы Linux, / etc / passwd всегда должен быть читабельным. попробуйте, затем с более общим порядком. – GL2014 13 July 2017 в 20:11

Вот мой ответ. Чистый питон. Использование timeit кажется довольно быстрым. Запуск 100 строк файла журнала, который имеет 100 000 строк:

>>> timeit.timeit('tail.tail(f, 100, 4098)', 'import tail; f = open("log.txt", "r");', number=10)
0.0014600753784179688
>>> timeit.timeit('tail.tail(f, 100, 4098)', 'import tail; f = open("log.txt", "r");', number=100)
0.00899195671081543
>>> timeit.timeit('tail.tail(f, 100, 4098)', 'import tail; f = open("log.txt", "r");', number=1000)
0.05842900276184082
>>> timeit.timeit('tail.tail(f, 100, 4098)', 'import tail; f = open("log.txt", "r");', number=10000)
0.5394978523254395
>>> timeit.timeit('tail.tail(f, 100, 4098)', 'import tail; f = open("log.txt", "r");', number=100000)
5.377126932144165

Вот код:

import os


def tail(f, lines=1, _buffer=4098):
    """Tail a file and get X lines from the end"""
    # place holder for the lines found
    lines_found = []

    # block counter will be multiplied by buffer
    # to get the block size from the end
    block_counter = -1

    # loop until we find X lines
    while len(lines_found) < lines:
        try:
            f.seek(block_counter * _buffer, os.SEEK_END)
        except IOError:  # either file is too small, or too many lines requested
            f.seek(0)
            lines_found = f.readlines()
            break

        lines_found = f.readlines()

        # we found enough lines, get out
        # Removed this line because it was redundant the while will catch
        # it, I left it for history
        # if len(lines_found) > lines:
        #    break

        # decrement the block counter to get the
        # next X bytes
        block_counter -= 1

    return lines_found[-lines:]
19
ответ дан glenbot 21 August 2018 в 08:55
поделиться
  • 1
    Элегантное решение! Действительно ли if len(lines_found) > lines: необходим? Не поймает ли это условие loop? – Maximilian Peters 23 July 2016 в 08:45
  • 2
    Вопрос для моего понимания: os.SEEK_END используется просто для ясности? Насколько я нашел, его значение постоянное (= 2). Мне было интересно оставить его, чтобы уйти из import os. Спасибо за отличное решение! – n1k31t4 5 October 2017 в 10:51
  • 3
    @MaximilianPeters да. Это необязательно. Я прокомментировал это. – glenbot 6 October 2017 в 14:21
  • 4
    @DexterMorgan вы можете заменить os.SEEK_END на его целочисленный эквивалент. Это было главным образом для удобства чтения. – glenbot 6 October 2017 в 14:22

Не первый пример, использующий deque, но более простой. Этот общий: он работает с любым итерируемым объектом, а не только с файлом.

#!/usr/bin/env python
import sys
import collections
def tail(iterable, N):
    deq = collections.deque()
    for thing in iterable:
        if len(deq) >= N:
            deq.popleft()
        deq.append(thing)
    for thing in deq:
        yield thing
if __name__ == '__main__':
    for line in tail(sys.stdin,10):
        sys.stdout.write(line)
0
ответ дан Hal Canary 21 August 2018 в 08:55
поделиться

Еще более чистая версия, совместимая с python3, которая не вставляет, а добавляет & amp; reverse:

def tail(f, window=1):
    """
    Returns the last `window` lines of file `f` as a list of bytes.
    """
    if window == 0:
        return b''
    BUFSIZE = 1024
    f.seek(0, 2)
    end = f.tell()
    nlines = window + 1
    data = []
    while nlines > 0 and end > 0:
        i = max(0, end - BUFSIZE)
        nread = min(end, BUFSIZE)

        f.seek(i)
        chunk = f.read(nread)
        data.append(chunk)
        nlines -= chunk.count(b'\n')
        end -= nread
    return b'\n'.join(b''.join(reversed(data)).splitlines()[-window:])

использовать его следующим образом:

with open(path, 'rb') as f:
    last_lines = tail(f, 3).decode('utf-8')
3
ответ дан Hauke Rehfeld 21 August 2018 в 08:55
поделиться
  • 1
    Не слишком потрепанный - но я бы вообще посоветовал не добавлять ответ на 10-летний вопрос с большим количеством ответов. Но помогите мне: что конкретно для Python 3 в вашем коде? – usr2564301 4 January 2018 в 02:58
  • 2
    Другие ответы были не совсем корректными :-) py3: см. stackoverflow.com/questions/136168/… – Hauke Rehfeld 4 January 2018 в 22:38

Предположим, что на 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]
69
ответ дан Ivan Castellanos 21 August 2018 в 08:55
поделиться
  • 1
    Должна быть независимой от платформы. Кроме того, если вы прочитаете вопрос, вы увидите, что f является файлом, подобным объекту. – Armin Ronacher 25 September 2008 в 22:57
  • 2
    в вопросе не сказано, что зависимость от платформы неприемлема. я не понимаю, почему это заслуживает двух downvotes, когда он обеспечивает очень unixy (может быть, то, что вы ищете ... конечно, было для меня), способ сделать именно то, что задает вопрос. – Shabbyrobe 3 June 2009 в 05:27
  • 3
    Спасибо, я думал, что должен был решить это на чистом Python, но нет причин не использовать утилиты UNIX, когда они под рукой, поэтому я пошел с этим. FWIW в современном Python, subprocess.check_output, вероятно, предпочтительнее os.popen2; это немного упрощает вещи, поскольку он просто возвращает результат в виде строки и поднимается на ненулевой код выхода. – mrooney 30 October 2013 в 03:35
  • 4
    Хотя это зависит от платформы, это эффективный способ very делать то, что было задано, а также быть чрезвычайно быстрым способом сделать это (вам не нужно загружать весь файл в память ). @Shabbyrobe – earthmeLon 21 November 2014 в 23:53
  • 5
    Возможно, вы захотите предусмотреть смещение как: offset_total = str(n+offset) и заменить эту строку stdin,stdout = os.popen2("tail -n "+offset_total+" "+f), чтобы избежать TypeErrors (cannot concatenate int+str) – AddingColor 8 October 2016 в 15:47
abc = "2018-06-16 04:45:18.68"
filename = "abc.txt"
with open(filename) as myFile:
    for num, line in enumerate(myFile, 1):
        if abc in line:
            lastline = num
print "last occurance of work at file is in "+str(lastline) 
0
ответ дан Kant Manapure 21 August 2018 в 08:55
поделиться

Мне пришлось прочитать определенное значение из последней строки файла и наткнулся на этот поток. Вместо того, чтобы изобретать колесо в Python, у меня был крошечный скрипт оболочки, сохраненный как / usr / local / bin / get_last_netp:

#! /bin/bash
tail -n1 /home/leif/projects/transfer/export.log | awk {'print $14'}

И в программе Python:

from subprocess import check_output

last_netp = int(check_output("/usr/local/bin/get_last_netp"))
0
ответ дан Leifbk 21 August 2018 в 08:55
поделиться

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

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

111
ответ дан Mark Amery 21 August 2018 в 08:55
поделиться
  • 1
    Это не удается для небольших лог-файлов - IOErrror: недопустимый аргумент - f.seek (block * 1024, 2) – ohnoes 4 December 2009 в 12:19
  • 2
    Очень хороший подход. Я использовал слегка модифицированную версию кода выше и придумал этот рецепт: code.activestate.com/recipes/577968-log-watcher-tail-floglog – Giampaolo Rodolà 29 November 2011 в 21:32
  • 3
    Больше не работает в python 3.2. Я получаю io.UnsupportedOperation: can't do nonzero end-relative seeks, я могу изменить смещение на 0, но это побеждает цель функции. – Logical Fallacy 1 May 2012 в 22:27
  • 4
    Причина @DavidEnglund здесь . Вкратце: поиск по отношению к концу файла не разрешен в текстовом режиме, предположительно потому, что содержимое файла необходимо декодировать, и, в общем, поиск произвольной позиции в последовательности закодированных байтов может иметь неопределенные результаты, когда вы попытаться декодировать в Unicode, начиная с этой позиции. Предложение, предлагаемое по ссылке, - попытаться открыть файл в двоичном режиме и выполнить декодирование самостоятельно, перехватывая исключения DecodeError. – max 10 September 2012 в 20:23
  • 5
    НЕ ИСПОЛЬЗУЙТЕ ЭТОТ КОД. Он повреждает строки в некоторых пограничных случаях в python 2.7. Ответ от @papercrane ниже исправляет это. – xApple 29 April 2013 в 11:19

Я нашел Popen выше лучшим решением. Это быстро и грязно, и это работает. Для python 2.6 на машине Unix я использовал следующий

    def GetLastNLines(self, n, fileName):
    """
    Name:           Get LastNLines
    Description:        Gets last n lines using Unix tail
    Output:         returns last n lines of a file
    Keyword argument:
    n -- number of last lines to return
    filename -- Name of the file you need to tail into
    """
    p=subprocess.Popen(['tail','-n',str(n),self.__fileName], stdout=subprocess.PIPE)
    soutput,sinput=p.communicate()
    return soutput

soutput будет содержать последние n строк кода. выполнить итерацию по строке soutput line:

for line in GetLastNLines(50,'myfile.log').split('\n'):
    print line
3
ответ дан Marko 21 August 2018 в 08:55
поделиться

это так просто:

def tail(fname,nl):
with open(fname) as f:
    data=f.readlines() #readlines return a list
    print(''.join(data[-nl:]))
0
ответ дан Med sadek 21 August 2018 в 08:55
поделиться
import time

attemps = 600
wait_sec = 5
fname = "YOUR_PATH"

with open(fname, "r") as f:
    where = f.tell()
    for i in range(attemps):
        line = f.readline()
        if not line:
            time.sleep(wait_sec)
            f.seek(where)
        else:
            print line, # already has newline
0
ответ дан moylop260 21 August 2018 в 08:55
поделиться

Хотя это не очень эффективно с большими файлами, этот код довольно прямолинейный:

  1. Он читает файл-объект, f.
  2. Он разбивает строку, возвращенную с использованием новых строк, \n.
  3. Он получает список последних индексов, используя отрицательный знак для обозначения последних индексов, и : для получения подмассива.
    def tail(f,n):
        return "\n".join(f.read().split("\n")[-n:])
    
-3
ответ дан ProgramFast 21 August 2018 в 08:55
поделиться
  • 1
    Человек, который отклонил мой ответ, не могли бы вы объяснить, почему? – ProgramFast 10 June 2016 в 22:28
  • 2
    в самый первый момент, когда вы используете f.read(), а не искать в обработчике файлов, вы помещаете ВСЕ свой файл в память. Буферизация всего файла (и не поиск) WRONG, поэтому ваши аннуляторы не добавляют ничего нового в проблему, а просто еще один способ наполнить вашу память. Теперь попробуйте использовать свой код с файлом 10gb, а затем посмотрите, что произойдет. Использование itertools - еще один способ попробовать, но оба seek и tail будут делать трюк. Вам не нужно класть все свои строки в память для их обработки, вы можете поместить их в куски. Надеюсь, вы понимаете. – erm3nda 27 December 2016 в 14:28
  • 3
    Эта функция не должна быть завершенной функцией. Это был всего лишь худший сценарий. – ProgramFast 27 December 2016 в 15:23

вы можете перейти к концу вашего файла с помощью f.seek (0, 2), а затем прочитать строки поочередно со следующей заменой readline ():

def readline_backwards(self, f):
    backline = ''
    last = ''
    while not last == '\n':
        backline = last + backline
        if f.tell() <= 0:
            return backline
        f.seek(-1, 1)
        last = f.read(1)
        f.seek(-1, 1)
    backline = last
    last = ''
    while not last == '\n':
        backline = last + backline
        if f.tell() <= 0:
            return backline
        f.seek(-1, 1)
        last = f.read(1)
        f.seek(-1, 1)
    f.seek(1, 1)
    return backline
1
ответ дан rabbit 21 August 2018 в 08:55
поделиться
This is my version of tailf

import sys, time, os

filename = 'path to file'

try:
    with open(filename) as f:
        size = os.path.getsize(filename)
        if size < 1024:
            s = size
        else:
            s = 999
        f.seek(-s, 2)
        l = f.read()
        print l
        while True:
            line = f.readline()
            if not line:
                time.sleep(1)
                continue
            print line
except IOError:
    pass
0
ответ дан Raj 21 August 2018 в 08:55
поделиться

С другой стороны, это, вероятно, так же быстро, как и все здесь.

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 21 August 2018 в 08:55
поделиться
  • 1
    Поскольку почти все здесь не работает с файлами журнала с объемом более 30 МБ или без него, не загружая в ОЗУ такой же объем памяти;) Ваша первая версия намного лучше, но для тестовых файлов здесь она работает немного хуже, чем моя и он не работает с разными символами новой строки. – Armin Ronacher 25 September 2008 в 23:06
  • 2
    Я был неправ. Версия 1 заняла 0,00248908996582 для 10 хвостов через словарь. Версия 2 приняла 1.2963051796 для 10 хвостов через словарь. Я почти проголосовал. – S.Lott 25 September 2008 в 23:06
  • 3
    "не работает с разными символами новой строки. & quot; Замените datacount ('\n') на len (data.splitlines ()), если это имеет значение. – S.Lott 25 September 2008 в 23:15

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

Для файла значительного размера mmap - лучший способ сделать это. Чтобы улучшить существующий ответ mmap, эта версия переносима между Windows и Linux и должна работать быстрее (хотя она не будет работать без каких-либо изменений на 32-битном Python с файлами в диапазоне GB, см. other ответ для подсказок по обработке этого и для изменения работы на Python 2 ).

import io  # Gets consistent version of open for both Py2.7 and Py3.x
import itertools
import mmap

def skip_back_lines(mm, numlines, startidx):
    '''Factored out to simplify handling of n and offset'''
    for _ in itertools.repeat(None, numlines):
        startidx= mm.rfind(b'\n', 0, startidx)
        if startidx< 0:
            break
    return startidx

def tail(f, n, offset=0):
    # Reopen file in binary mode
    with io.open(f.name, 'rb') as binf, mmap.mmap(binf.fileno(), 0, access=mmap.ACCESS_READ) as mm:
        # len(mm) - 1 handles files ending w/newline by getting the prior line
        startofline = skip_back_lines(mm, offset, len(mm) - 1)
        if startofline < 0:
            return []  # Offset lines consumed whole file, nothing to return
            # If using a generator function (yield-ing, see below),
            # this should be a plain return, no empty list

        endoflines = startofline + 1  # Slice end to omit offset lines

        # Find start of lines to capture (add 1 to move from newline to beginning of following line)
        startofline = skip_back_lines(mm, n, startofline) + 1

        # Passing True to splitlines makes it return the list of lines without
        # removing the trailing newline (if any), so list mimics f.readlines()
        return mm[startofline:endoflines].splitlines(True)
        # If Windows style \r\n newlines need to be normalized to \n, and input
        # is ASCII compatible, can normalize newlines with:
        # return mm[startofline:endoflines].replace(os.linesep.encode('ascii'), b'\n').splitlines(True)

Это предполагает, что количество хвостов хвоста достаточно мало, вы можете спокойно прочитать их все в памяти на один раз; вы также можете сделать это функцией генератора и вручную прочитать строку за раз, заменив последнюю строку:

        mm.seek(startofline)
        # Call mm.readline n times, or until EOF, whichever comes first
        for line in itertools.islice(iter(mm.readline, b''), n):
            yield line

Наконец, это прочитано в двоичном режиме (необходимо использовать mmap), чтобы он дает строки str (Py2) и bytes (Py3); если вы хотите unicode (Py2) или str (Py3), итеративный подход может быть изменен для декодирования для вас и / или исправления новых строк:

        lines = itertools.islice(iter(mm.readline, b''), n)
        if f.encoding:  # Decode if the passed file was opened with a specific encoding
            lines = (line.decode(f.encoding) for line in lines)
        if 'b' not in f.mode:  # Fix line breaks if passed file opened in text mode
            lines = (line.replace(os.linesep, '\n') for line in lines)
        for line in lines:
            yield line

Примечание: я набрал все это на машине, где мне не хватает доступа к Python для тестирования. Пожалуйста, дайте мне знать, если я что-то опечалил; это было похоже на мой другой ответ , что я думаю, он должен работать, но твики (например, обработка offset) могут привести к тонким ошибкам. Пожалуйста, дайте мне знать в комментариях, если есть какие-либо ошибки.

2
ответ дан ShadowRanger 21 August 2018 в 08:55
поделиться

Есть несколько существующих реализаций хвоста на pypi, который вы можете установить с помощью pip:

  • mtFileUtil
  • multitail
  • log4tailer
  • ...

В зависимости от вашей ситуации могут быть преимущества использования одного из этих существующих инструментов.

2
ответ дан Travis Bear 21 August 2018 в 08:55
поделиться

Я нашел, пожалуй, самый простой способ найти первую или последнюю N строк файла

Последние N строк файла (для Ex: N = 10)

file=open("xyz.txt",'r")
liner=file.readlines()
for ran in range((len(liner)-N),len(liner)):
    print liner[ran]

Первые N строк файла (для Ex: N = 10)

file=open("xyz.txt",'r")
liner=file.readlines()
for ran in range(0,N+1):
    print liner[ran]
0
ответ дан user9956608 21 August 2018 в 08:55
поделиться
import itertools
fname = 'log.txt'
offset = 5
n = 10
with open(fname) as f:
    n_last_lines = list(reversed([x for x in itertools.islice(f, None)][-(offset+1):-(offset+n+1):-1]))
0
ответ дан Y Kal 21 August 2018 в 08:55
поделиться
Другие вопросы по тегам:

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