Odczyt pliku w odwrotnej kolejności za pomocą Pythona

Jak odczytać plik w odwrotnej kolejności za pomocą Pythona? Chcę przeczytać plik od ostatniej linii do pierwszej linii.

Author: Nick Volynkin, 2010-02-20

15 answers

for line in reversed(open("filename").readlines()):
    print line.rstrip()

I w Pythonie 3:

for line in reversed(list(open("filename"))):
    print(line.rstrip())
 59
Author: Matt Joiner,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-07 03:24:21

Poprawna, skuteczna odpowiedź napisana jako generator.

import os

def reverse_readline(filename, buf_size=8192):
    """a generator that returns the lines of a file in reverse order"""
    with open(filename) as fh:
        segment = None
        offset = 0
        fh.seek(0, os.SEEK_END)
        file_size = remaining_size = fh.tell()
        while remaining_size > 0:
            offset = min(file_size, offset + buf_size)
            fh.seek(file_size - offset)
            buffer = fh.read(min(remaining_size, buf_size))
            remaining_size -= buf_size
            lines = buffer.split('\n')
            # the first line of the buffer is probably not a complete line so
            # we'll save it and append it to the last line of the next buffer
            # we read
            if segment is not None:
                # if the previous chunk starts right from the beginning of line
                # do not concact the segment to the last line of new chunk
                # instead, yield the segment first 
                if buffer[-1] is not '\n':
                    lines[-1] += segment
                else:
                    yield segment
            segment = lines[0]
            for index in range(len(lines) - 1, 0, -1):
                if len(lines[index]):
                    yield lines[index]
        # Don't yield None if the file was empty
        if segment is not None:
            yield segment
 100
Author: srohde,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-17 08:35:08

A może coś takiego:

import os


def readlines_reverse(filename):
    with open(filename) as qfile:
        qfile.seek(0, os.SEEK_END)
        position = qfile.tell()
        line = ''
        while position >= 0:
            qfile.seek(position)
            next_char = qfile.read(1)
            if next_char == "\n":
                yield line[::-1]
                line = ''
            else:
                line += next_char
            position -= 1
        yield line[::-1]


if __name__ == '__main__':
    for qline in readlines_reverse(raw_input()):
        print qline

Ponieważ plik jest odczytywany znak po znaku w odwrotnej kolejności, będzie działał nawet na bardzo dużych plikach, o ile poszczególne linie mieszczą się w pamięci.

 16
Author: Berislav Lopac,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-11-05 01:09:22

Możesz również użyć modułu Pythona file_read_backwards.

Po zainstalowaniu, poprzez pip install file_read_backwards (v1.2.1), Możesz odczytać cały plik wstecz (pod względem liniowym) w pamięci efektywny sposób poprzez:

#!/usr/bin/env python2.7

from file_read_backwards import FileReadBackwards

with FileReadBackwards("/path/to/file", encoding="utf-8") as frb:
    for l in frb:
         print l
Obsługuje kodowanie"utf-8"," latin-1 "i" ascii".

Wsparcie jest również dostępne dla python3. Dalsze dokumenty można znaleźć na stronie http://file-read-backwards.readthedocs.io/en/latest/readme.html

 9
Author: user7321751,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-28 02:06:47
for line in reversed(open("file").readlines()):
    print line.rstrip()

Jeśli korzystasz z Linuksa, możesz użyć komendy tac.

$ tac file

2 przepisy można znaleźć w ActiveState tutaj i tutaj

 8
Author: ghostdog74,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-02-20 11:11:00
import re

def filerev(somefile, buffer=0x20000):
  somefile.seek(0, os.SEEK_END)
  size = somefile.tell()
  lines = ['']
  rem = size % buffer
  pos = max(0, (size // buffer - 1) * buffer)
  while pos >= 0:
    somefile.seek(pos, os.SEEK_SET)
    data = somefile.read(rem + buffer) + lines[0]
    rem = 0
    lines = re.findall('[^\n]*\n?', data)
    ix = len(lines) - 2
    while ix > 0:
      yield lines[ix]
      ix -= 1
    pos -= buffer
  else:
    yield lines[0]

with open(sys.argv[1], 'r') as f:
  for line in filerev(f):
    sys.stdout.write(line)
 8
Author: Ignacio Vazquez-Abrams,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-05-07 02:20:59

Tutaj znajdziesz moją implementację, możesz ograniczyć użycie pamięci ram zmieniając zmienną "buffer", jest błąd, że program wypisuje pustą linię na początku.

A także użycie pamięci ram może być zwiększone, jeśli nie ma nowych linii na więcej niż bajty bufora, zmienna" leak " będzie wzrastać aż do zobaczenia nowej linii ("\n").

To działa również dla plików 16 GB, które są większe niż moja całkowita pamięć.

import os,sys
buffer = 1024*1024 # 1MB
f = open(sys.argv[1])
f.seek(0, os.SEEK_END)
filesize = f.tell()

division, remainder = divmod(filesize, buffer)
line_leak=''

for chunk_counter in range(1,division + 2):
    if division - chunk_counter < 0:
        f.seek(0, os.SEEK_SET)
        chunk = f.read(remainder)
    elif division - chunk_counter >= 0:
        f.seek(-(buffer*chunk_counter), os.SEEK_END)
        chunk = f.read(buffer)

    chunk_lines_reversed = list(reversed(chunk.split('\n')))
    if line_leak: # add line_leak from previous chunk to beginning
        chunk_lines_reversed[0] += line_leak

    # after reversed, save the leakedline for next chunk iteration
    line_leak = chunk_lines_reversed.pop()

    if chunk_lines_reversed:
        print "\n".join(chunk_lines_reversed)
    # print the last leaked line
    if division - chunk_counter < 0:
        print line_leak
 2
Author: Bekir Dogan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-27 18:01:17

Dzięki za odpowiedź @srohde. Ma mały błąd sprawdzania znaku nowej linii z operatorem 'is', I nie mogłem skomentować odpowiedzi z reputacją 1. Również chciałbym zarządzać plikami otwartymi na zewnątrz, ponieważ umożliwia mi to osadzanie moich bełkotów w zadaniach luigi.

To, co musiałem zmienić, ma postać:

with open(filename) as fp:
    for line in fp:
        #print line,  # contains new line
        print '>{}<'.format(line)
Chciałbym zmienić na:
with open(filename) as fp:
    for line in reversed_fp_iter(fp, 4):
        #print line,  # contains new line
        print '>{}<'.format(line)

Tutaj jest zmodyfikowana odpowiedź, która chce obsługi pliku i zachowuje nowe linie:

def reversed_fp_iter(fp, buf_size=8192):
    """a generator that returns the lines of a file in reverse order
    ref: https://stackoverflow.com/a/23646049/8776239
    """
    segment = None  # holds possible incomplete segment at the beginning of the buffer
    offset = 0
    fp.seek(0, os.SEEK_END)
    file_size = remaining_size = fp.tell()
    while remaining_size > 0:
        offset = min(file_size, offset + buf_size)
        fp.seek(file_size - offset)
        buffer = fp.read(min(remaining_size, buf_size))
        remaining_size -= buf_size
        lines = buffer.splitlines(True)
        # the first line of the buffer is probably not a complete line so
        # we'll save it and append it to the last line of the next buffer
        # we read
        if segment is not None:
            # if the previous chunk starts right from the beginning of line
            # do not concat the segment to the last line of new chunk
            # instead, yield the segment first
            if buffer[-1] == '\n':
                #print 'buffer ends with newline'
                yield segment
            else:
                lines[-1] += segment
                #print 'enlarged last line to >{}<, len {}'.format(lines[-1], len(lines))
        segment = lines[0]
        for index in range(len(lines) - 1, 0, -1):
            if len(lines[index]):
                yield lines[index]
    # Don't yield None if the file was empty
    if segment is not None:
        yield segment
 2
Author: Murat Yükselen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-14 15:18:16

Prosta funkcja do tworzenia drugiego pliku odwróconego (tylko linux):

import os
def tac(file1, file2):
     print(os.system('tac %s > %s' % (file1,file2)))

Jak używać

tac('ordered.csv', 'reversed.csv')
f = open('reversed.csv')
 2
Author: Alexandre Andrade,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-07 05:24:25

Jeśli martwisz się o rozmiar pliku / użycie pamięci, mapowanie pamięci pliku i skanowanie wstecz w poszukiwaniu nowych linii jest rozwiązaniem:

Jak wyszukać ciąg znaków w plikach tekstowych?

 1
Author: Federico,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-08 10:29:46
def reverse_lines(filename):
    y=open(filename).readlines()
    return y[::-1]
 0
Author: Gareema,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-13 05:32:01

Zawsze używaj with podczas pracy z plikami, ponieważ obsługuje wszystko za Ciebie:

with open('filename', 'r') as f:
    for line in reversed(f.readlines()):
        print line

Lub w Pythonie 3:

with open('filename', 'r') as f:
    for line in reversed(list(f.readlines())):
        print(line)
 0
Author: Carlos Afonso,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-02 15:19:59

Najpierw trzeba otworzyć plik w formacie odczytu, zapisać go do zmiennej, a następnie otworzyć drugi plik w formacie zapisu, w którym można zapisać lub dołączyć zmienną za pomocą plasterka [:: -1] , całkowicie odwracając plik. Możesz również użyć metody readlines (), aby przekształcić ją w listę linii, którą możesz manipulować

def copy_and_reverse(filename, newfile):
    with open(filename) as file:
        text = file.read()
    with open(newfile, "w") as file2:
        file2.write(text[::-1])
 0
Author: PawlakJ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-07 12:47:47

Większość odpowiedzi musi przeczytać cały plik przed zrobieniem czegokolwiek. Próbka ta odczytuje coraz większe próbki od końca .

Widziałem tylko odpowiedź Murata Yükselena pisząc tę odpowiedź. To prawie to samo, co, jak sądzę, jest dobrą rzeczą. Poniższy przykład dotyczy również \r i zwiększa jego rozmiar bufora na każdym kroku. Mam też kilka testów jednostkowych , aby potwierdzić ten kod.

def readlines_reversed(f):
    """ Iterate over the lines in a file in reverse. The file must be
    open in 'rb' mode. Yields the lines unencoded (as bytes), including the
    newline character. Produces the same result as readlines, but reversed.
    If this is used to reverse the line in a file twice, the result is
    exactly the same.
    """
    head = b""
    f.seek(0, 2)
    t = f.tell()
    buffersize, maxbuffersize = 64, 4096
    while True:
        if t <= 0:
            break
        # Read next block
        buffersize = min(buffersize * 2, maxbuffersize)
        tprev = t
        t = max(0, t - buffersize)
        f.seek(t)
        lines = f.read(tprev - t).splitlines(True)
        # Align to line breaks
        if not lines[-1].endswith((b"\n", b"\r")):
            lines[-1] += head  # current tail is previous head
        elif head == b"\n" and lines[-1].endswith(b"\r"):
            lines[-1] += head  # Keep \r\n together
        elif head:
            lines.append(head)
        head = lines.pop(0)  # can be '\n' (ok)
        # Iterate over current block in reverse
        for line in reversed(lines):
            yield line
    if head:
        yield head
 0
Author: Almar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-10 21:35:22

Musiałem to zrobić jakiś czas temu i użyłem poniższego kodu. Do muszli. Obawiam się, że nie mam już kompletnego scenariusza. Jeśli korzystasz z systemu operacyjnego unixish, możesz użyć "tac", jednak na przykład Mac OSX polecenie tac nie działa, użyj tail-r. poniższy fragment kodu testuje, na której platformie się znajdujesz, i odpowiednio dostosowuje polecenie

# We need a command to reverse the line order of the file. On Linux this
# is 'tac', on OSX it is 'tail -r'
# 'tac' is not supported on osx, 'tail -r' is not supported on linux.

if sys.platform == "darwin":
    command += "|tail -r"
elif sys.platform == "linux2":
    command += "|tac"
else:
    raise EnvironmentError('Platform %s not supported' % sys.platform)
 -2
Author: jeorgen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-12-07 23:20:03