Bir dosya ters sırada nasıl okunur?


128

Python kullanarak bir dosyayı ters sırada nasıl okuyabilirim? Son satırdan ilk satıra kadar bir dosya okumak istiyorum.


7
"Ters sırada oku" mu yoksa "satırları ters sırada işle" mi demek istiyorsun? Bir fark var. İlki ile, potansiyel olarak dosya aynı anda belleğe sığmaz, bu nedenle satırları ters sırada işlemek istersiniz, ancak tüm dosyayı okuyup tersine çeviremezsiniz. İkincisi ile, tüm dosyayı içinde okuyabilir ve işlemeden önce satırların listesini tersine çevirebilirsiniz. Öyleyse hangisi?
Lasse V.Karlsen


1
Bunu tavsiye ederim - bellek sorunları yok ve hızlı: stackoverflow.com/a/260433/1212562
Brian B

Yanıtlar:


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

Ve Python 3'te:

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

192
Ne yazık ki, tüm dosyayı belleğe sığdıramazsanız bu işe yaramaz.
vy32

3
Ayrıca, gönderilen kod soruyu yanıtlarken, açtığımız dosyaları kapatmaya dikkat etmeliyiz. withİfadesi genellikle oldukça ağrısızdır.
William

1
@MichaelDavidWatson: Orijinal yineleyiciyi önce belleğe okumadan ve ardından ilk yineleyicinin tersine yeni bir yineleyici sunmadan olmaz.
Matt Joiner

3
@MichaelDavidWatson: Bir dosyayı hafızaya okumadan tersten okuyabilirsiniz, ancak bu önemsiz değildir ve hatırı sayılır sistem çağrısı israfını önlemek için çok sayıda tampon belleği gerektirir. Ayrıca çok kötü bir performans sergileyecektir (dosya kullanılabilir belleği aşarsa belleğin tamamını belleğe okumaktan daha iyi olsa da).
Matt Joiner

1
@William Pardon, dosya üzerinde yinelerken "açık" seçeneğini kullanarak yukarıdaki çözümü nasıl kullanabilirim ve sonra onu temizleyip kapatabilirim?
BringBackCommodore64

146

Oluşturucu olarak yazılmış doğru, etkili bir cevap.

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 concat the segment to the last line of new chunk.
                # Instead, yield the segment first 
                if buffer[-1] != '\n':
                    lines[-1] += segment
                else:
                    yield segment
            segment = lines[0]
            for index in range(len(lines) - 1, 0, -1):
                if lines[index]:
                    yield lines[index]
        # Don't yield None if the file was empty
        if segment is not None:
            yield segment

4
Python> = 3.2'deki metin dosyaları için bu işe yaramaz , çünkü bazı nedenlerden dolayı dosyanın sonuna göre aramalar artık desteklenmiyor. Tarafından döndürülen dosyanın boyutunu kaydederek sabitlenebilir fh.seek(0, os.SEEK_END)ve değişen fh.seek(-offset, os.SEEK_END)çok fh.seek(file_size - offset).
levesque

9
Yapılan düzenlemelerden sonra bu, python 3.5'te mükemmel şekilde çalışır. Soruya en iyi cevap.
notbad.jpeg

3
dönmek bu değişikliği piton 2 için nereye fh.seek()dönerNone
marengaz

1
Bunun metin dosyaları için beklendiği gibi çalışmayabileceğine dikkat edin. Blokları ters sırada doğru şekilde almak yalnızca ikili dosyalar için işe yarar. Sorun, çok baytlı kodlamaya (örneğin utf8) sahip metin dosyaları için seek()ve read()farklı boyutlara başvurmasıdır. Muhtemelen aynı zamanda sıfır olmayan ilk argüman nedeni budur seek()göreli için os.SEEK_ENDdesteklenmez.
norok2

3
basit: 'aöaö'.encode()is b'a\xc3\xb6a\xc3\xb6'. Bunu diske kaydedip daha sonra metin modunda okursanız, yaptığınızda seek(2)iki bayt hareket eder, bu da seek(2); read(1)bir hataya neden olur UnicodeDecodeError: 'utf-8' codec can't decode byte 0xb6 in position 0: invalid start byte, ancak yaparsanız, beklediğiniz seek(0); read(2); read(1)şeyi alırsınız 'a', yani: seek()asla kodlamaz -aware, read()dosyayı metin modunda açmanızdır . Şimdi varsa 'aöaö' * 1000000, bloklarınız doğru şekilde hizalanmayacaktır.
norok2

23

Bunun gibi bir şeye ne dersiniz:

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

Dosya karakter karakter ters sırada okunduğundan, tek tek satırlar belleğe sığdığı sürece çok büyük dosyalarda bile çalışacaktır.


20

Ayrıca python modülünü de kullanabilirsiniz file_read_backwards.

pip install file_read_backwardsYükledikten sonra, (v1.2.1) aracılığıyla , tüm dosyayı geriye doğru (satır bazında) bellek açısından verimli bir şekilde şu şekilde okuyabilirsiniz:

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

"Utf-8", "latin-1" ve "ascii" kodlamalarını destekler.

Python3 için de destek mevcuttur. Daha fazla belge http://file-read-backwards.readthedocs.io/en/latest/readme.html adresinde bulunabilir.


Bu çözüm için teşekkürler. Nasıl yapıldığını anlamama yardımcı olduğu için @srohde tarafından yukarıdaki çözümü beğendim (ve ayrıca oy verdim), ancak bir geliştirici olarak elimden geldiğince mevcut bir modülü kullanmayı tercih ediyorum, bu yüzden bunu bilmekten mutluyum.
joanis

1
Bu, UTF-8 gibi çok baytlı kodlama ile çalışır. Arama / okuma çözümü şunları yapmaz: seek () bayt olarak sayar, karakter olarak oku ().
Jeremitu

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

Linux'taysanız, taccommand kullanabilirsiniz .

$ tac file

ActiveState'te burada ve burada bulabileceğiniz 2 tarif


1
Reversed () yinelemeden önce tüm diziyi tüketiyor mu merak ediyorum. Dokümanlar, bir __reversed__()yöntemin gerekli olduğunu söylüyor , ancak python2.5, o olmadan özel bir sınıftan şikayet etmiyor.
muhuk

@muhuk, muhtemelen bir şekilde önbelleğe alması gerekiyor, ters sırada yeni bir liste oluşturduğundan ve buna bir yineleyici döndürdüğünden şüpheleniyorum
Matt Joiner,

1
@Matt: Bu çok saçma olurdu. Basitçe arkadan öne gider - len (L) -1 arka, 0 ön taraftır. Gerisini hayal edebilirsiniz.
Devin Jeanpierre

@muhuk: Diziler anlamlı bir şekilde tüketilmiyor (dizinin tamamını yineleyebilirsiniz, ancak çok da önemli değil). Bir __reversed__yöntem de gerekli değildir ve böyle bir şeyin de faydası yoktu. Bir nesne sağlarsa __len__ve __getitem__iyi çalışırsa (dikte gibi bazı istisnai durumlar hariç).
Devin Jeanpierre

@Devin jeanpierre: readlines () sağlayan bir nesne döndürür Sadece eğer __reversed__?
Matt Joiner

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

Bu, arabellekten daha büyük dosyalar için yanlış çıktı üretiyor gibi görünüyor. Anladığım kadarıyla, okuduğunuz arabellek büyüklüğündeki yığınları kapsayan satırları doğru şekilde işlemez. Başka bir benzer yanıt gönderdim (başka bir benzer soruya).
Darius Bacon

@Darius: Ah evet, biraz kaçırdım galiba. Şimdi düzeltilmelidir.
Ignacio Vazquez-Abrams

Doğru görünüyor. Yine de kendi kodumu tercih ederim çünkü bu O (N ^ 2) tek bir uzun satırdan oluşan büyük bir dosyada çalışır. (Test ettiğim diğer soruya verilen benzer cevaplarda, bu tür dosyalarda ciddi bir yavaşlamaya neden oldu.)
Darius Bacon

3
Soru performanstan bahsetmedi, bu yüzden normal ifadeler olan performans felaketini niteleyemem: P
Matt Joiner

Performans olarak biraz daha açıklama yararlı olabilir ve eğer bu gerçekten son satırı söyleyip sadece o parçayı okuyabilirse.
user1767754

7

Kabul edilen yanıt, belleğe sığmayan büyük dosyaların olduğu durumlarda işe yaramaz (bu nadir bir durum değildir).

Başkalarının da belirttiği gibi @srohde yanıtı iyi görünüyor, ancak sonraki sorunları var:

  • dosya nesnesini geçirip hangi kodlamada okunması gerektiğine karar vermek için kullanıcıya bıraktığımızda dosya açma gereksiz görünüyor,
  • dosya nesnesini kabul etmek için yeniden düzenleme yapsak bile, bu tüm kodlamalar için çalışmayacaktır: utf-8kodlamalı ve ascii olmayan içerikli dosyayı seçebiliriz.

    й

    geçmesi buf_sizeeşittir 1ve olacak

    UnicodeDecodeError: 'utf8' codec can't decode byte 0xb9 in position 0: invalid start byte

    elbette metin daha büyük buf_sizeolabilir, ancak alınabilir, bu nedenle yukarıdaki gibi karmaşık hatalara yol açar,

  • özel satır ayırıcı belirtemiyoruz,
  • satır ayırıcıyı tutmayı seçemeyiz.

Tüm bu endişeleri göz önünde bulundurarak ayrı işlevler yazdım:

  • bayt akışlarıyla çalışan biri,
  • ikincisi, metin akışlarıyla çalışan ve temelindeki bayt akışını birinciye devredip ortaya çıkan satırların kodunu çözen.

Her şeyden önce bir sonraki fayda fonksiyonlarını tanımlayalım:

ceil_divisiontavanla bölme yapmak için ( //zeminle standart bölmenin aksine , bu başlıkta daha fazla bilgi bulunabilir )

def ceil_division(left_number, right_number):
    """
    Divides given numbers with ceiling.
    """
    return -(-left_number // right_number)

split sağ uçtan verilen ayırıcıya göre dizeyi ayırmak için:

def split(string, separator, keep_separator):
    """
    Splits given string by given separator.
    """
    parts = string.split(separator)
    if keep_separator:
        *parts, last_part = parts
        parts = [part + separator for part in parts]
        if last_part:
            return parts + [last_part]
    return parts

read_batch_from_end ikili akışın sağ ucundan toplu okumak için

def read_batch_from_end(byte_stream, size, end_position):
    """
    Reads batch from the end of given byte stream.
    """
    if end_position > size:
        offset = end_position - size
    else:
        offset = 0
        size = end_position
    byte_stream.seek(offset)
    return byte_stream.read(size)

Bundan sonra bayt akışını ters sırada okumak için işlev tanımlayabiliriz.

import functools
import itertools
import os
from operator import methodcaller, sub


def reverse_binary_stream(byte_stream, batch_size=None,
                          lines_separator=None,
                          keep_lines_separator=True):
    if lines_separator is None:
        lines_separator = (b'\r', b'\n', b'\r\n')
        lines_splitter = methodcaller(str.splitlines.__name__,
                                      keep_lines_separator)
    else:
        lines_splitter = functools.partial(split,
                                           separator=lines_separator,
                                           keep_separator=keep_lines_separator)
    stream_size = byte_stream.seek(0, os.SEEK_END)
    if batch_size is None:
        batch_size = stream_size or 1
    batches_count = ceil_division(stream_size, batch_size)
    remaining_bytes_indicator = itertools.islice(
            itertools.accumulate(itertools.chain([stream_size],
                                                 itertools.repeat(batch_size)),
                                 sub),
            batches_count)
    try:
        remaining_bytes_count = next(remaining_bytes_indicator)
    except StopIteration:
        return

    def read_batch(position):
        result = read_batch_from_end(byte_stream,
                                     size=batch_size,
                                     end_position=position)
        while result.startswith(lines_separator):
            try:
                position = next(remaining_bytes_indicator)
            except StopIteration:
                break
            result = (read_batch_from_end(byte_stream,
                                          size=batch_size,
                                          end_position=position)
                      + result)
        return result

    batch = read_batch(remaining_bytes_count)
    segment, *lines = lines_splitter(batch)
    yield from reverse(lines)
    for remaining_bytes_count in remaining_bytes_indicator:
        batch = read_batch(remaining_bytes_count)
        lines = lines_splitter(batch)
        if batch.endswith(lines_separator):
            yield segment
        else:
            lines[-1] += segment
        segment, *lines = lines
        yield from reverse(lines)
    yield segment

ve son olarak metin dosyasını tersine çevirmek için bir işlev şu şekilde tanımlanabilir:

import codecs


def reverse_file(file, batch_size=None, 
                 lines_separator=None,
                 keep_lines_separator=True):
    encoding = file.encoding
    if lines_separator is not None:
        lines_separator = lines_separator.encode(encoding)
    yield from map(functools.partial(codecs.decode,
                                     encoding=encoding),
                   reverse_binary_stream(
                           file.buffer,
                           batch_size=batch_size,
                           lines_separator=lines_separator,
                           keep_lines_separator=keep_lines_separator))

Testler

Hazırlıklar

Şu fsutilkomutu kullanarak 4 dosya oluşturdum :

  1. içeriği olmayan empty.txt , boyut 0MB
  2. 1MB boyutunda tiny.txt
  3. 10MB boyutunda small.txt
  4. 50MB boyutunda large.txt

Ayrıca @srohde çözümünü dosya yolu yerine dosya nesnesiyle çalışacak şekilde yeniden düzenledim.

Test komut dosyası

from timeit import Timer

repeats_count = 7
number = 1
create_setup = ('from collections import deque\n'
                'from __main__ import reverse_file, reverse_readline\n'
                'file = open("{}")').format
srohde_solution = ('with file:\n'
                   '    deque(reverse_readline(file,\n'
                   '                           buf_size=8192),'
                   '          maxlen=0)')
azat_ibrakov_solution = ('with file:\n'
                         '    deque(reverse_file(file,\n'
                         '                       lines_separator="\\n",\n'
                         '                       keep_lines_separator=False,\n'
                         '                       batch_size=8192), maxlen=0)')
print('reversing empty file by "srohde"',
      min(Timer(srohde_solution,
                create_setup('empty.txt')).repeat(repeats_count, number)))
print('reversing empty file by "Azat Ibrakov"',
      min(Timer(azat_ibrakov_solution,
                create_setup('empty.txt')).repeat(repeats_count, number)))
print('reversing tiny file (1MB) by "srohde"',
      min(Timer(srohde_solution,
                create_setup('tiny.txt')).repeat(repeats_count, number)))
print('reversing tiny file (1MB) by "Azat Ibrakov"',
      min(Timer(azat_ibrakov_solution,
                create_setup('tiny.txt')).repeat(repeats_count, number)))
print('reversing small file (10MB) by "srohde"',
      min(Timer(srohde_solution,
                create_setup('small.txt')).repeat(repeats_count, number)))
print('reversing small file (10MB) by "Azat Ibrakov"',
      min(Timer(azat_ibrakov_solution,
                create_setup('small.txt')).repeat(repeats_count, number)))
print('reversing large file (50MB) by "srohde"',
      min(Timer(srohde_solution,
                create_setup('large.txt')).repeat(repeats_count, number)))
print('reversing large file (50MB) by "Azat Ibrakov"',
      min(Timer(azat_ibrakov_solution,
                create_setup('large.txt')).repeat(repeats_count, number)))

Not : collections.dequeJeneratörü tüketmek için sınıf kullandım .

çıktılar

Windows 10'da PyPy 3.5 için:

reversing empty file by "srohde" 8.31e-05
reversing empty file by "Azat Ibrakov" 0.00016090000000000028
reversing tiny file (1MB) by "srohde" 0.160081
reversing tiny file (1MB) by "Azat Ibrakov" 0.09594989999999998
reversing small file (10MB) by "srohde" 8.8891863
reversing small file (10MB) by "Azat Ibrakov" 5.323388100000001
reversing large file (50MB) by "srohde" 186.5338368
reversing large file (50MB) by "Azat Ibrakov" 99.07450229999998

Windows 10'da CPython 3.5 için:

reversing empty file by "srohde" 3.600000000000001e-05
reversing empty file by "Azat Ibrakov" 4.519999999999958e-05
reversing tiny file (1MB) by "srohde" 0.01965560000000001
reversing tiny file (1MB) by "Azat Ibrakov" 0.019207699999999994
reversing small file (10MB) by "srohde" 3.1341862999999996
reversing small file (10MB) by "Azat Ibrakov" 3.0872588000000007
reversing large file (50MB) by "srohde" 82.01206720000002
reversing large file (50MB) by "Azat Ibrakov" 82.16775059999998

Gördüğümüz gibi, orijinal çözüm gibi çalışır, ancak daha geneldir ve yukarıda listelenen dezavantajlarından arındırılmıştır.


İlan

Bunu , birçok iyi test edilmiş işlevsel / yinelenen yardımcı programa sahip paket0.3.0 sürümüne ( Python 3.5 + gerektirir ) ekledim .lz

Gibi kullanılabilir

 import io
 from lz.iterating import reverse
 ...
 with open('path/to/file') as file:
     for line in reverse(file, batch_size=io.DEFAULT_BUFFER_SIZE):
         print(line)

Tüm standart kodlamaları destekler ( utf-7bununla kodlanabilir dizeler oluşturmak için bir strateji tanımlamanın benim için zor olması dışında ).


2

Burada benim uygulamamı bulabilirsin, "buffer" değişkenini değiştirerek ram kullanımını sınırlayabilirsin, programın başlangıçta boş bir satır yazdırması gibi bir hata var.

Ayrıca tampon baytından fazla yeni satır yoksa ram kullanımı artabilir, "sızıntı" değişkeni yeni bir satır ("\ n") görene kadar artacaktır.

Bu aynı zamanda toplam belleğimden daha büyük olan 16 GB'lık dosyalar için de çalışıyor.

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

Cevabınız için teşekkürler @srohde. 'İs' operatörü ile satırsonu karakterini kontrol eden küçük bir hata var ve cevaba 1 itibarla yorum yapamadım. Ayrıca dışarıda açık olan dosyayı yönetmek istiyorum çünkü bu, luigi görevleri için başıboş gezintilerimi yerleştirmemi sağlıyor.

Değiştirmem gereken şey şu forma sahip:

with open(filename) as fp:
    for line in fp:
        #print line,  # contains new line
        print '>{}<'.format(line)

Değiştirmeyi çok isterim:

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

İşte bir dosya tanıtıcısı isteyen ve yeni satırları tutan değiştirilmiş bir cevap:

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

1

tersine ikinci bir dosya oluşturmak için basit bir işlev (yalnızca linux):

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

nasıl kullanılır

tac('ordered.csv', 'reversed.csv')
f = open('reversed.csv')

Bence amaç, bunu Python'da nasıl yapacağımızdı. Ayrıca, bu sadece * Nix sistemlerinde çalışır, ancak bunun için mükemmel bir çözümdür. Temelde Python'u kabuk yardımcı programlarını çalıştırmak için bir komut olarak kullanıyor.
Alexander Huszagh

1
Bu kodda şu anda yazıldığı gibi büyük güvenlik hataları var. mv mycontent.txt $'hello $(rm -rf $HOME) world.txt'Güvenilmeyen bir kullanıcı tarafından verilen bir çıktı dosyası adı kullanılarak veya benzer şekilde oluşturulan bir dosyayı tersine çevirmeye çalışıyorsanız ne olur ? Rasgele dosya adlarını güvenli bir şekilde kullanmak istiyorsanız, daha dikkatli olmanız gerekir. subprocess.Popen(['tac', file1], stdout=open(file2, 'w'))örneğin güvenli olacaktır.
Charles Duffy

Mevcut kod ayrıca boşluk, joker karakter ve c içeren dosyaları doğru şekilde işlemez.
Charles Duffy


1

f olarak open ("dosya adı") ile:

    print(f.read()[::-1])

Bu tüm dosyayı okuyor mu? Bu büyük dosyalarda güvenli midir? Bu, bunu yapmanın çok kolay ve gerçekçi bir yolu gibi görünüyor, ancak yukarıdaki sorulardan emin değilim .. Dosyayı bu şekilde (yeniden kullanarak) aramak isterdim ..
ikwyl6

@ ikwyl6 Bu eşdeğer olmalıdır list(reversed(f.read())).
AMC


0

withSizin için her şeyi hallettiği için dosyalarla çalışırken her zaman kullanın :

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

Veya Python 3'te:

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

0

önce dosyanızı okuma biçiminde açmanız, onu bir değişkene kaydetmeniz, ardından ikinci dosyayı yazma biçiminde açmanız ve değişkeni [:: - 1] dilimini kullanarak değişkeni tamamen tersine çevirerek yazmanız veya eklemeniz gerekir. Ayrıca, işleyebileceğiniz bir satır listesi yapmak için readlines () kullanabilirsiniz.

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

Cevapların çoğunun herhangi bir şey yapmadan önce tüm dosyayı okuması gerekir. Bu örnek giderek daha büyük örnekleri okur ucundan .

Murat Yükselen'in cevabını sadece bu cevabı yazarken gördüm. Neredeyse aynı, sanırım iyi bir şey. Aşağıdaki örnek ayrıca \ r ile ilgilenir ve her adımda tampon boyutunu artırır. Ayrıca bu kodu yedeklemek için bazı birim testlerim var .

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

Dosyayı satır satır okuyun ve ardından ters sırada bir listeye ekleyin.

İşte bir kod örneği:

reverse = []
with open("file.txt", "r") as file:
    for line in file:
        line = line.strip()
         reverse[0:0] = line

Bu, kabul edilen cevapta çözümün daha düşük bir versiyonu gibi görünüyor .
AMC


0
def previous_line(self, opened_file):
        opened_file.seek(0, os.SEEK_END)
        position = opened_file.tell()
        buffer = bytearray()
        while position >= 0:
            opened_file.seek(position)
            position -= 1
            new_byte = opened_file.read(1)
            if new_byte == self.NEW_LINE:
                parsed_string = buffer.decode()
                yield parsed_string
                buffer = bytearray()
            elif new_byte == self.EMPTY_BYTE:
                continue
            else:
                new_byte_array = bytearray(new_byte)
                new_byte_array.extend(buffer)
                buffer = new_byte_array
        yield None

kullanmak:

opened_file = open(filepath, "rb")
iterator = self.previous_line(opened_file)
line = next(iterator) #one step
close(opened_file)

-3

Bunu bir süre önce yapmak zorunda kaldım ve aşağıdaki kodu kullandım. Kabuğa borular. Korkarım artık tam metne sahip değilim. Unixish işletim sistemindeyseniz, "tac" kullanabilirsiniz, ancak örneğin Mac OSX tac komutu çalışmaz, tail -r kullanın. Aşağıdaki kod parçacığı, hangi platformda olduğunuzu test eder ve komutu buna göre ayarlar.

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

Poster bir python cevabı arıyor.
mikemaccana

Tamamlanmamış gibi görünse de bu bir Python cevabıdır.
DrDee

2
değil, çapraz platform değil, sistem komutlarını kullanıyor = pitonik değil
Phyo Arkar Lwin

Poster, kod parçacığının gerçekten de yazılmış olduğu "python kullanarak" bir yanıt arıyor. Ancak bunun, yayınlanan diğer birçok çözümle karşılaştırıldığında çok iyi bir çözüm olmadığını kabul ediyorum.
jeorgen

1
Snippet, doğruluğu değerlendirmek için yeterince eksiksiz değil (çağrının diğer bölümleri gösterilmemiştir), ancak dizelerde kabuk komutlarını depolamak son derece şüphelidir - çok fazla şey almadığı sürece kabuk enjeksiyon hatalarının olması kolaydır Bakım.
Charles Duffy
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.