Laatste n regels van een bestand ophalen, vergelijkbaar met staart

Ik schrijf een logbestandviewer voor een webtoepassing en daarvoor wil ik door de regels van het logbestand pagineren. De items in het bestand zijn gebaseerd op regels met het nieuwste item onderaan.

Dus ik heb een tail()methode nodig die Nregels vanaf de onderkant kan lezen en een offset kan ondersteunen. Dit is de hoed die ik bedacht:

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

Is dit een redelijke benadering? Wat is de aanbevolen manier om logbestanden met offsets te volgen?


Antwoord 1, autoriteit 100%

Dit kan sneller zijn dan de jouwe. Doet geen aannames over lijnlengte. Gaat één blok tegelijk door het bestand totdat het het juiste aantal ‘\n’-tekens heeft gevonden.

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:])

Ik hou niet van lastige aannames over de lengte van een lijn, terwijl je – praktisch gezien – dat soort dingen nooit kunt weten.

Over het algemeen lokaliseert dit de laatste 20 regels op de eerste of tweede passage door de lus. Als je ding van 74 tekens echt klopt, maak je de blokgrootte 2048 en heb je bijna onmiddellijk 20 regels achter.

Bovendien verbrand ik niet veel hersencalorieën om de afstemming met fysieke OS-blokkades te verfijnen. Als je deze I/O-pakketten op hoog niveau gebruikt, betwijfel ik of je enige prestatieconsequenties zult zien van het proberen af ​​te stemmen op OS-blokgrenzen. Als u I/O op een lager niveau gebruikt, ziet u mogelijk een versnelling.


UPDATE

voor Python 3.2 en hoger, volg het proces op bytes zoals In tekstbestanden (die geopend zonder een “b”in de modustekenreeks), zoekt alleen relatief ten opzichte van het begin van de bestanden zijn toegestaan ​​(met uitzondering van het zoeken naar het einde van het bestand met seek(0, 2)).:

bv: f = open('C:/.../../apache_logs.txt', 'rb')

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 = []
    while lines_to_go > 0 and block_end_byte > 0:
        if (block_end_byte - BLOCK_SIZE > 0):
            f.seek(block_number*BLOCK_SIZE, 2)
            blocks.append(f.read(BLOCK_SIZE))
        else:
            f.seek(0,0)
            blocks.append(f.read(block_end_byte))
        lines_found = blocks[-1].count(b'\n')
        lines_to_go -= lines_found
        block_end_byte -= BLOCK_SIZE
        block_number -= 1
    all_read_text = b''.join(reversed(blocks))
    return b'\n'.join(all_read_text.splitlines()[-total_lines_wanted:])

Antwoord 2, autoriteit 76%

Er wordt vanuit gegaan dat een Unix-achtig systeem op Python 2 het volgende kan doen:

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]

Voor python 3 kunt u het volgende doen:

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]

Antwoord 3, autoriteit 29%

Hier is mijn antwoord. Puur python. Met timeit lijkt het vrij snel. 100 regels volgen van een logbestand met 100.000 regels:

>>> 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

Hier is de code:

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:]

Antwoord 4, autoriteit 27%

Als het lezen van het hele bestand acceptabel is, gebruik dan een deque.

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

Vóór 2.6 hadden deques geen maxlen-optie, maar het is eenvoudig genoeg om te implementeren.

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

Als het een vereiste is om het bestand vanaf het einde te lezen, gebruik dan een galop (ook wel exponentieel) zoeken.

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:]

Antwoord 5, autoriteit 20%

S.Lott’s antwoord hierboven werkt bijna voor mij, maar geeft me uiteindelijk gedeeltelijke regels. Het blijkt dat het gegevens op blokgrenzen corrumpeert omdat gegevens de leesblokken in omgekeerde volgorde houden. Wanneer ”.join(data) wordt aangeroepen, staan ​​de blokken in de verkeerde volgorde. Dit lost dat op.

def tail(f, window=20):
    """
    Returns the last `window` lines of file `f` as a list.
    f - a byte file-like object
    """
    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:]

Antwoord 6, autoriteit 18%

De code die ik uiteindelijk heb gebruikt. Ik denk dat dit de beste is tot nu toe:

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

Antwoord 7, autoriteit 11%

Eenvoudige en snelle oplossing met 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()

Antwoord 8, autoriteit 5%

De eenvoudigste manier is om dequete gebruiken:

from collections import deque
def tail(filename, n=10):
    with open(filename) as f:
        return deque(f, n)

Antwoord 9, autoriteit 4%

Update @papercrane-oplossing naar python3.
Open het bestand met open(filename, 'rb')en:

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:]

Antwoord 10, autoriteit 4%

Een antwoord plaatsen in opdracht van reageerders op mijn antwoord op een vergelijkbare vraagwaarbij dezelfde techniek werd gebruikt om de laatste regel van een bestand te muteren, snap het niet zomaar.

Voor een bestand van aanzienlijke omvang, mmapis de beste manier om dit te doen. Om het bestaande mmap-antwoord te verbeteren, is deze versie overdraagbaar tussen Windows en Linux, en zou sneller moeten werken (hoewel het niet zal werken zonder enkele aanpassingen op 32-bits Python met bestanden in het GB-bereik, zie het andere antwoord voor hints om hiermee om te gaan en voor het aanpassen om te werken op 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)

Hierbij wordt ervan uitgegaan dat het aantal achterliggende regels klein genoeg is dat u ze veilig allemaal tegelijk in het geheugen kunt lezen; je kunt hier ook een generatorfunctie van maken en regel voor regel handmatig lezen door de laatste regel te vervangen door:

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

Ten slotte leest dit in binaire modus (noodzakelijk om mmapte gebruiken) zodat het strregels (Py2) en bytesregels (Py3 ); als je unicode(Py2) of str(Py3) wilt, kan de iteratieve aanpak worden aangepast om voor je te decoderen en/of nieuwe regels te repareren:

       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)
        # Python 3.2 and earlier:
        for line in lines:
            yield line
        # 3.3+:
        yield from lines

Opmerking: ik heb dit allemaal getypt op een machine waar ik geen toegang heb tot Python om te testen. Laat het me weten als ik iets heb getypt; dit was vergelijkbaar genoeg met mijn andere antwoorddat ik denkdat het zou moeten werken, maar de tweaks (bijv. omgaan met een offset) kan leiden tot subtiele fouten. Laat het me weten in de reacties als er fouten zijn.


Antwoord 11, autoriteit 3%

Een nog schonere python3-compatibele versie die niet invoegt, maar toevoegt & keert om:

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:])

gebruik het als volgt:

with open(path, 'rb') as f:
    last_lines = tail(f, 3).decode('utf-8')

Antwoord 12, autoriteit 2%

gebaseerd op het meest gestemde antwoord van S.Lott (25 september ’08 om 21:43), maar opgelost voor kleine bestanden.

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

Ik hoop dat dit nuttig is.


Antwoord 13, autoriteit 2%

Er zijn enkele bestaande implementaties van tail on pypi die u kunt installeren met pip:

  • mtFileUtil
  • meerstaart
  • log4tailer

Afhankelijk van uw situatie kunnen er voordelen zijn aan het gebruik van een van deze bestaande tools.


Antwoord 14, autoriteit 2%

Eenvoudig:

with open("test.txt") as f:
data = f.readlines()
tail = data[-2:]
print(''.join(tail)

Antwoord 15, autoriteit 2%

Ik vond de Popen hierboven de beste oplossing. Het is snel en vies en het werkt
Voor python 2.6 op Unix-machine heb ik het volgende gebruikt

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 zal de laatste n regels van de code bevatten. om regel voor regel door de uitgang te lopen:

for line in GetLastNLines(50,'myfile.log').split('\n'):
    print line

Antwoord 16

Voor efficiëntie met zeer grote bestanden (gebruikelijk in logfile-situaties waarin u mogelijk tail wilt gebruiken), wilt u over het algemeen vermijden het hele bestand te lezen (zelfs als u het doet zonder het hele bestand in één keer in het geheugen te lezen). , moet u op de een of andere manier de offset in regels in plaats van in tekens uitwerken. Een mogelijkheid is achteruit lezen met seek() char voor char, maar dit is erg traag. In plaats daarvan is het beter om in grotere blokken te verwerken.

Ik heb een hulpprogramma-functie die ik een tijdje geleden heb geschreven om bestanden achterstevoren te lezen die hier kan worden gebruikt.

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] Toegevoegd meer specifieke versie (vermijdt moeten twee keer achteruit)


Antwoord 17

U kunt naar het einde van uw bestand met f.seek (0, 2) en vervolgens afgelezen lijnen een voor een met de volgende vervanging voor 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

Antwoord 18

Op basis van Eyecue antwoord (10 juni ’10 op 21:28):. Deze klasse add head () en de staart () gebruikt om het bestand object

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:]

Gebruik:

f = File('path/to/file', 'r')
f.head(3)
f.tail(3)

Antwoord 19

Een aantal van deze oplossingen hebben problemen als het bestand niet eindigt in \ n of in het waarborgen van de volledige eerste lijn wordt gelezen.

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

Antwoord 20

Hier is een vrij eenvoudige implementatie:

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()

Antwoord 21

Er is een zeer nuttige moduledie dit kan:

from file_read_backwards import FileReadBackwards
with FileReadBackwards("/tmp/file", encoding="utf-8") as frb:
# getting lines by lines starting from the last line up
for l in frb:
    print(l)

Antwoord 22

Update voor antwoord gegeven door A.Coady

Werkt met python 3.

Dit gebruikt Exponentieel zoekenen buffert alleen Nregels van achteren en is zeer efficiënt.

import time
import os
import sys
def tail(f, n):
    assert n >= 0
    pos, lines = n+1, []
    # set file pointer to end
    f.seek(0, os.SEEK_END)
    isFileSmall = False
    while len(lines) <= n:
        try:
            f.seek(f.tell() - pos, os.SEEK_SET)
        except ValueError as e:
            # lines greater than file seeking size
            # seek to start
            f.seek(0,os.SEEK_SET)
            isFileSmall = True
        except IOError:
            print("Some problem reading/seeking the file")
            sys.exit(-1)
        finally:
            lines = f.readlines()
            if isFileSmall:
                break
        pos *= 2
    print(lines)
    return lines[-n:]
with open("stream_logs.txt") as f:
    while(True):
        time.sleep(0.5)
        print(tail(f,2))

Antwoord 23

Ik moest een specifieke waarde uit de laatste regel van een bestand lezen en kwam deze thread tegen. In plaats van het wiel opnieuw uit te vinden in Python, eindigde ik met een klein shellscript, opgeslagen als
/usr/local/bin/get_last_netp:

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

En in het Python-programma:

from subprocess import check_output
last_netp = int(check_output("/usr/local/bin/get_last_netp"))

Antwoord 24

Niet het eerste voorbeeld met een deque, maar een eenvoudiger voorbeeld. Deze is algemeen: hij werkt op elk willekeurig object, niet alleen op een bestand.

#!/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)

Antwoord 25

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

Antwoord 26

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

Antwoord 27

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]))

Antwoord 28

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) 

Antwoord 29

Een andere oplossing

als uw txt-bestand er als volgt uitziet:
muis
slang
kat
hagedis
wolf
hond

je zou dit bestand kunnen terugdraaien door simpelweg array-indexering in python te gebruiken
”’

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)

resultaat:
hond
wolf
hagedis
kat


Antwoord 30

Nou! Ik had een soortgelijk probleem, hoewel ik alleen ALLEEN LAATSTE REGELnodig had,
dus bedacht ik mijn eigen oplossing

def get_last_line(filepath):
    try:
        with open(filepath,'rb') as f:
            f.seek(-1,os.SEEK_END)
            text = [f.read(1)]
            while text[-1] != '\n'.encode('utf-8') or len(text)==1:
                f.seek(-2, os.SEEK_CUR)
                text.append(f.read(1))
    except Exception as e:
        pass
    return ''.join([t.decode('utf-8') for t in text[::-1]]).strip()

Deze functie retourneert de laatste tekenreeks in een bestand
Ik heb een logbestand van 1,27 GB en het kostte heel weinig tijd om de laatste regel te vinden (nog geen halve seconde)

Other episodes