Bagaimana cara membaca file dalam urutan terbalik?

128

Bagaimana cara membaca file dalam urutan terbalik menggunakan python? Saya ingin membaca file dari baris terakhir ke baris pertama.

Nimmy
sumber
7
Apakah maksud Anda "membacanya dalam urutan terbalik" atau "memproses garis dalam urutan terbalik"? Ada perbedaan. Dengan yang pertama, kemungkinan file tidak akan muat dalam memori semua pada saat yang sama, jadi Anda ingin memproses baris dalam urutan terbalik, tetapi Anda tidak dapat membaca seluruh file dan membalikkannya. Dengan yang kedua, Anda mungkin baru saja membaca seluruh file, dan membalikkan daftar baris sebelum memprosesnya. Jadi yang mana?
Lasse V. Karlsen
1
Saya merekomendasikan ini - tidak ada masalah memori dan cepat: stackoverflow.com/a/260433/1212562
Brian B

Jawaban:

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

Dan dengan Python 3:

for line in reversed(list(open("filename"))):
    print(line.rstrip())
Matt Joiner
sumber
192
Sayangnya, ini tidak berfungsi jika Anda tidak dapat memasukkan seluruh file dalam memori.
vy32
3
Selain itu, sementara kode yang diposting menjawab pertanyaan, kita harus berhati-hati untuk menutup file yang kita buka. The withPernyataan biasanya cukup menyakitkan.
William
1
@MichaelDavidWatson: Bukan tanpa membaca iterator asli ke dalam memori terlebih dahulu dan kemudian menghadirkan iterator baru di atas yang pertama secara terbalik.
Matt Joiner
3
@MichaelDavidWatson: Anda dapat membaca file secara terbalik tanpa membacanya ke dalam memori tetapi tidak trivial dan membutuhkan banyak buffer shenanigans untuk menghindari pemborosan panggilan sistem yang cukup besar. Ini juga akan berkinerja sangat buruk (walaupun lebih baik daripada membaca seluruh memori menjadi memori jika file melebihi memori yang tersedia).
Matt Joiner
1
@ William Maaf, bagaimana saya menggunakan solusi di atas menggunakan "dengan terbuka" sementara iterasi file dan kemudian bersih-tutup?
BringBackCommodore64
146

Jawaban yang benar dan efisien ditulis sebagai 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 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
srohde
sumber
4
Itu tidak akan berfungsi untuk file teks dalam python> = 3.2, karena untuk beberapa alasan mencari relatif ke akhir file tidak lagi didukung. Dapat diperbaiki dengan menyimpan ukuran file yang dikembalikan oleh fh.seek(0, os.SEEK_END)dan mengubah fh.seek(-offset, os.SEEK_END)juga fh.seek(file_size - offset).
levesque
9
Setelah pengeditan dilakukan, ini berfungsi dengan sempurna di python 3.5. Jawaban terbaik untuk pertanyaan itu.
notbad.jpeg
3
kembali perubahan ini untuk python 2 di mana fh.seek()pengembalianNone
marengaz
1
Perhatikan bahwa ini mungkin tidak berfungsi seperti yang diharapkan untuk file teks. Mendapatkan blok dengan benar dalam urutan terbalik hanya berfungsi untuk file biner. Masalahnya adalah untuk file teks dengan pengkodean multi-byte (seperti utf8), seek()dan read()merujuk ke ukuran yang berbeda. Itu mungkin juga alasan mengapa argumen pertama non-nol seek()relatif os.SEEK_ENDtidak didukung.
norok2
3
sederhana: 'aöaö'.encode()adalah b'a\xc3\xb6a\xc3\xb6'. Jika Anda menyimpan ini ke disk dan kemudian membaca dalam mode teks, ketika Anda melakukannya seek(2)akan bergerak dua byte, sehingga seek(2); read(1)akan menghasilkan kesalahan UnicodeDecodeError: 'utf-8' codec can't decode byte 0xb6 in position 0: invalid start byte, tetapi jika Anda melakukannya seek(0); read(2); read(1), Anda akan mendapatkan yang 'a'Anda harapkan, yaitu: seek()tidak pernah menyandikan -adar, read()adalah jika Anda membuka file dalam mode teks. Sekarang jika sudah 'aöaö' * 1000000, blok Anda tidak akan disejajarkan dengan benar.
norok2
23

Bagaimana dengan sesuatu yang seperti ini:

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

Karena file tersebut dibaca karakter demi karakter dalam urutan terbalik, itu akan bekerja bahkan pada file yang sangat besar, selama masing-masing baris masuk ke dalam memori.

Berislav Lopac
sumber
20

Anda juga dapat menggunakan modul python file_read_backwards.

Setelah menginstalnya, melalui pip install file_read_backwards(v1.2.1), Anda dapat membaca seluruh file mundur (sesuai jalur) dengan cara yang efisien memori melalui:

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

Mendukung pengkodean "utf-8", "latin-1", dan "ascii".

Dukungan juga tersedia untuk python3. Dokumentasi lebih lanjut dapat ditemukan di http://file-read-backwards.readthedocs.io/en/latest/readme.html

pengguna7321751
sumber
Terima kasih atas solusi ini. Saya suka (dan juga memutakhirkan) solusi di atas oleh @srohde karena itu membantu saya memahami bagaimana hal itu dilakukan, tetapi sebagai pengembang saya lebih suka menggunakan modul yang ada ketika saya bisa, jadi saya senang mengetahui tentang yang ini.
joanis
1
Ini berfungsi dengan pengkodean multibyte seperti UTF-8. Solusi seek / read tidak: seek () dihitung dalam byte, baca () dalam karakter.
Jeremitu
9
for line in reversed(open("file").readlines()):
    print line.rstrip()

Jika Anda menggunakan linux, Anda dapat menggunakan tacperintah.

$ tac file

2 resep yang dapat Anda temukan di ActiveState di sini dan di sini

ghostdog74
sumber
1
Saya bertanya-tanya apakah terbalik () mengkonsumsi seluruh urutan sebelum iterasi. Documents mengatakan __reversed__()metode diperlukan, tetapi python2.5 tidak mengeluh pada kelas khusus tanpa itu.
muhuk
@ Muhuk, mungkin harus men-cache-nya, saya curiga ini membuat daftar baru dalam urutan terbalik kemudian mengembalikan iterator ke sana
Matt Joiner
1
@ Matt: itu akan konyol. Itu hanya pergi dari belakang ke depan - len (L) -1 adalah belakang, 0 adalah depan. Anda bisa membayangkan sisanya.
Devin Jeanpierre
@ Muhuk: Urutan tidak dikonsumsi secara bermakna (Anda dapat beralih di seluruh urutan, tetapi tidak terlalu penting). Suatu __reversed__metode juga tidak perlu, dan tidak ada gunanya menjadi seperti itu. Jika suatu objek menyediakan __len__dan __getitem__itu akan berfungsi dengan baik (dikurangi beberapa kasus luar biasa, seperti dict).
Devin Jeanpierre
@Devin Jeanpierre: Hanya jika readlines () mengembalikan objek yang menyediakan __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)
Ignacio Vazquez-Abrams
sumber
Ini tampaknya menghasilkan output yang salah untuk file yang lebih besar dari buffer. Itu tidak akan dengan benar menangani garis yang menjangkau bongkahan berukuran buffer yang Anda baca, seperti yang saya mengerti. Saya memposting jawaban serupa lainnya (untuk pertanyaan serupa lainnya).
Darius Bacon
@Darius: Ah ya, sepertinya saya sedikit ketinggalan. Harus diperbaiki sekarang.
Ignacio Vazquez-Abrams
Terlihat benar. Saya masih lebih suka kode saya sendiri karena ini tidak O (N ^ 2) bekerja pada file besar yang semuanya satu baris panjang. (Dalam jawaban yang mirip dengan pertanyaan lain yang saya uji ini menyebabkan perlambatan asli yang serius pada file tersebut.)
Darius Bacon
3
Yah pertanyaannya tidak menyebutkan kinerja, jadi saya tidak bisa melihat bencana kinerja yang merupakan ekspresi reguler: P
Matt Joiner
Penjelasan lebih lanjut akan berguna sebagai kinerja dan jika ini benar-benar dapat mencoba untuk mengatakan baris terakhir dan membaca karya itu saja.
user1767754
7

Jawaban yang diterima tidak akan berfungsi untuk kasus dengan file besar yang tidak muat di memori (yang bukan kasus yang jarang terjadi).

Seperti yang dicatat oleh orang lain, jawaban @srohde terlihat bagus, tetapi memiliki masalah berikutnya:

  • file openning terlihat berlebihan, ketika kita dapat melewatkan objek file & menyerahkannya kepada pengguna untuk memutuskan encoding mana yang harus dibaca,
  • bahkan jika kita menolak untuk menerima objek file, itu tidak akan berfungsi untuk semua penyandian: kita dapat memilih file dengan utf-8penyandian dan konten non-ascii seperti

    й

    lulus buf_sizesama dengan 1dan akan dimiliki

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

    tentu saja teks mungkin lebih besar tetapi buf_sizedapat diambil sehingga akan menyebabkan kesalahan yang dikaburkan seperti di atas,

  • kami tidak dapat menentukan pemisah baris khusus,
  • kita tidak bisa memilih untuk menjaga garis pemisah.

Jadi mempertimbangkan semua masalah ini saya telah menulis fungsi terpisah:

  • yang bekerja dengan stream byte,
  • yang kedua yang bekerja dengan aliran teks dan mendelegasikan aliran byte yang mendasarinya ke yang pertama dan menerjemahkan baris yang dihasilkan.

Pertama-tama mari kita mendefinisikan fungsi utilitas selanjutnya:

ceil_divisionuntuk membuat pembagian dengan langit-langit (berbeda dengan //divisi standar dengan lantai, info lebih lanjut dapat ditemukan di utas ini )

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

split untuk memisahkan string dengan memberikan pemisah dari ujung kanan dengan kemampuan untuk menyimpannya:

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 untuk membaca kumpulan dari ujung kanan aliran biner

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)

Setelah itu kita dapat mendefinisikan fungsi untuk membaca aliran byte dalam urutan terbalik seperti

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

dan akhirnya fungsi untuk membalikkan file teks dapat didefinisikan seperti:

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

Tes

Persiapan

Saya telah menghasilkan 4 file menggunakan fsutilperintah :

  1. empty.txt tanpa isi, ukuran 0MB
  2. tiny.txt dengan ukuran 1MB
  3. small.txt dengan ukuran 10MB
  4. large.txt dengan ukuran 50MB

juga saya solusi refactored @srohde untuk bekerja dengan objek file, bukan path file.

Skrip uji

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

Catatan : Saya sudah menggunakan collections.dequeclass untuk knalpot generator.

Keluaran

Untuk PyPy 3.5 pada Windows 10:

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

Untuk CPython 3.5 di Windows 10:

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

Jadi seperti yang kita lihat itu berfungsi seperti solusi asli, tetapi lebih umum dan bebas dari kerugian yang tercantum di atas.


Iklan

Saya telah menambahkan ini ke 0.3.0versi lzpaket (membutuhkan Python 3.5 +) yang memiliki banyak utilitas fungsional / iterasi yang teruji.

Dapat digunakan seperti

 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)

Ini mendukung semua pengkodean standar (mungkin kecuali utf-7karena sulit bagi saya untuk menentukan strategi untuk menghasilkan string yang dikodekan dengannya).

Azat Ibrakov
sumber
2

Di sini Anda dapat menemukan implementasi saya, Anda dapat membatasi penggunaan ram dengan mengubah variabel "buffer", ada bug bahwa program mencetak baris kosong di awal.

Dan juga penggunaan ram dapat meningkat jika tidak ada baris baru untuk lebih dari buffer byte, variabel "kebocoran" akan meningkat hingga melihat baris baru ("\ n").

Ini juga berfungsi untuk file 16 GB yang lebih besar dari total memori saya.

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
Bekir Dogan
sumber
2

Terima kasih atas jawabannya @srohde. Ini memiliki bug kecil untuk memeriksa karakter baris baru dengan operator 'is', dan saya tidak bisa mengomentari jawaban dengan 1 reputasi. Saya juga ingin mengelola file terbuka di luar karena itu memungkinkan saya untuk menanamkan rambling saya untuk tugas-tugas luigi.

Apa yang perlu saya ubah adalah formulir:

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

Saya ingin mengubah ke:

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

Berikut ini adalah jawaban yang dimodifikasi yang menginginkan file menangani dan membuat baris baru:

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
Murat Yükselen
sumber
1

fungsi sederhana untuk membuat file kedua terbalik (hanya linux):

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

Cara Penggunaan

tac('ordered.csv', 'reversed.csv')
f = open('reversed.csv')
Alexandre Andrade
sumber
Saya pikir tujuannya adalah bagaimana melakukannya dengan Python. Plus, ini hanya bekerja pada sistem * Nix, meskipun ini solusi yang sangat baik untuk itu. Ini pada dasarnya hanya menggunakan Python sebagai prompt untuk menjalankan utilitas shell.
Alexander Huszagh
1
Kode ini memiliki bug keamanan utama seperti yang ditulis saat ini. Bagaimana jika Anda mencoba membalikkan file yang dibuat dengan mv mycontent.txt $'hello $(rm -rf $HOME) world.txt', atau sama-sama menggunakan nama file output yang diberikan oleh pengguna yang tidak dipercaya? Jika Anda ingin menangani nama file yang sewenang-wenang dengan aman, perlu lebih hati-hati. subprocess.Popen(['tac', file1], stdout=open(file2, 'w'))akan aman, misalnya.
Charles Duffy
Kode yang ada juga tidak menangani file dengan spasi, wildcard, & c dengan benar.
Charles Duffy
1

dengan open ("nama file") sebagai f:

    print(f.read()[::-1])
JackoM
sumber
Apakah ini membaca seluruh file? Apakah ini aman untuk file besar? Ini tampaknya menjadi cara yang sangat mudah dan realistis untuk melakukannya tetapi tidak yakin tentang pertanyaan di atas .. Saya ingin mencari file dengan cara ini (menggunakan kembali) ..
ikwyl6
@ ikwyl6 Ini harus sama dengan list(reversed(f.read())).
AMC
0
def reverse_lines(filename):
    y=open(filename).readlines()
    return y[::-1]
Gareema
sumber
Apakah ini pada dasarnya bukan solusi yang sama dengan jawaban yang diterima ?
AMC
0

Selalu gunakan withketika bekerja dengan file karena menangani segalanya untuk Anda:

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

Atau dengan Python 3:

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

Anda harus terlebih dahulu membuka file Anda dalam format baca, menyimpannya ke variabel, kemudian membuka file kedua dalam format tulis di mana Anda akan menulis atau menambahkan variabel menggunakan slice [:: - 1], membalikkan file sepenuhnya. Anda juga dapat menggunakan readlines () untuk membuatnya menjadi daftar baris, yang dapat Anda manipulasi

def copy_and_reverse(filename, newfile):
    with open(filename) as file:
        text = file.read()
    with open(newfile, "w") as file2:
        file2.write(text[::-1])
PawlakJ
sumber
0

Sebagian besar jawaban perlu membaca seluruh file sebelum melakukan apa pun. Sampel ini membaca sampel yang semakin besar dari akhir .

Saya hanya melihat jawaban Murat Yükselen saat menulis jawaban ini. Hampir sama, yang saya kira adalah hal yang baik. Sampel di bawah ini juga berkaitan dengan dan meningkatkan buffer pada setiap langkah. Saya juga memiliki beberapa unit test untuk mendukung kode ini.

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
Almar
sumber
0

Baca file baris demi baris dan kemudian tambahkan pada daftar dalam urutan terbalik.

Berikut ini contoh kode:

reverse = []
with open("file.txt", "r") as file:
    for line in file:
        line = line.strip()
         reverse[0:0] = line
willywonka
sumber
Ini sepertinya versi yang lebih rendah dari solusi dalam jawaban yang diterima .
AMC
0
import sys
f = open(sys.argv[1] , 'r')
for line in f.readlines()[::-1]:
    print line
Powerup California
sumber
Apakah ini pada dasarnya bukan solusi yang sama dengan jawaban yang diterima ?
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

menggunakan:

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

Saya harus melakukan ini beberapa waktu lalu dan menggunakan kode di bawah ini. Ini pipa ke shell. Saya khawatir saya tidak memiliki skrip yang lengkap lagi. Jika Anda menggunakan sistem operasi yang tidakix, Anda dapat menggunakan "tac", namun pada mis. Perintah Mac OSX tac tidak berfungsi, gunakan tail -r. Cuplikan kode di bawah ini menguji platform mana Anda berada, dan menyesuaikan perintah yang sesuai

# 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)
jeorgen
sumber
Poster itu mencari jawaban python.
mikemaccana
Yah, itu adalah jawaban Python meskipun tampaknya tidak lengkap.
DrDee
2
itu bukan, bukan crossplatform, menggunakan perintah sistem = bukan pythonic
Phyo Arkar Lwin
Poster itu mencari jawaban "menggunakan python", yang mana potongan kode memang ditulis. Tapi saya setuju itu bukan solusi yang sangat baik dibandingkan dengan banyak yang lain diposting.
jeorgen
1
Cuplikan tidak cukup lengkap untuk mengevaluasi kebenaran (bagian lain dari doa tidak ditampilkan), tetapi menyimpan perintah shell dalam string sangat mencurigakan - mudah untuk memiliki bug injeksi shell kecuali mengambil banyak sekali perawatan.
Charles Duffy