Làm thế nào để đọc một tập tin theo thứ tự ngược lại?


128

Làm thế nào để đọc một tập tin theo thứ tự ngược bằng python? Tôi muốn đọc một tập tin từ dòng cuối cùng đến dòng đầu tiên.


7
Bạn có nghĩa là "đọc nó theo thứ tự ngược" hoặc "xử lý các dòng theo thứ tự ngược"? Có một sự khác biệt. Với lần đầu tiên, có khả năng tệp sẽ không vừa trong bộ nhớ cùng một lúc, vì vậy bạn muốn xử lý các dòng theo thứ tự ngược lại, nhưng bạn không thể đọc toàn bộ tệp và đảo ngược nó. Với lần thứ hai, bạn có thể chỉ cần đọc toàn bộ tệp và đảo ngược danh sách các dòng trước khi xử lý chúng. Vậy đó là cái gì?
Lasse V. Karlsen


1
Tôi khuyên bạn nên điều này - không có vấn đề về bộ nhớ và nhanh chóng: stackoverflow.com/a/260433/1212562
Brian B

Câu trả lời:


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

Và trong Python 3:

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

192
Than ôi, điều này không hoạt động nếu bạn không thể vừa toàn bộ tập tin trong bộ nhớ.
vy32

3
Ngoài ra, trong khi mã được đăng không trả lời câu hỏi, chúng ta nên cẩn thận để đóng các tệp mà chúng ta mở. Các withtuyên bố thường khá đau đớn.
William

1
@MichaelDavidWatson: Không phải không đọc trình lặp ban đầu vào bộ nhớ trước và sau đó trình bày một trình vòng lặp mới qua trình lặp đầu tiên ngược lại.
Matt Joiner

3
@MichaelDavidWatson: Bạn có thể đọc một tệp ngược mà không cần đọc nó vào bộ nhớ nhưng nó không cần thiết và đòi hỏi nhiều shenanigans đệm để tránh lãng phí cuộc gọi hệ thống đáng kể. Nó cũng sẽ hoạt động rất tệ (mặc dù tốt hơn là đọc toàn bộ bộ nhớ vào bộ nhớ nếu tệp vượt quá bộ nhớ khả dụng).
Matt Joiner

1
@William Xin lỗi, làm cách nào để sử dụng giải pháp trên bằng cách sử dụng "với mở" trong khi lặp lại tệp và sau đó đóng sạch nó?
BringBackCoosore64

146

Một câu trả lời đúng, hiệu quả được viết như một máy phát điện.

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
Điều đó sẽ không hoạt động đối với các tệp văn bản trong python> = 3.2, vì một số lý do tìm kiếm liên quan đến cuối tệp không còn được hỗ trợ. Có thể được sửa bằng cách lưu kích thước của tệp được trả về fh.seek(0, os.SEEK_END)và thay đổi fh.seek(-offset, os.SEEK_END)quá fh.seek(file_size - offset).
levesque

9
Sau khi chỉnh sửa, nó hoạt động hoàn hảo trong python 3.5. Câu trả lời tốt nhất cho câu hỏi.
notbad.jpeg

3
hoàn nguyên thay đổi này cho python 2 khi fh.seek()trả vềNone
marengaz 15/03/2016

1
Xem ra rằng điều này có thể không hoạt động như mong đợi cho các tập tin văn bản. Bắt các khối chính xác theo thứ tự đảo ngược chỉ hoạt động cho các tệp nhị phân. Vấn đề là đối với các tệp văn bản có mã hóa nhiều byte (chẳng hạn như utf8) seek()read()tham khảo các kích thước khác nhau. Đó có lẽ cũng là lý do tại sao đối số đầu tiên khác không seek()liên quan đến os.SEEK_ENDkhông được hỗ trợ.
norok2

3
đơn giản: 'aöaö'.encode()b'a\xc3\xb6a\xc3\xb6'. Nếu bạn lưu nó vào đĩa và sau đó đọc ở chế độ văn bản, khi bạn thực hiện seek(2)nó sẽ di chuyển theo hai byte, do đó seek(2); read(1)sẽ gây ra lỗi UnicodeDecodeError: 'utf-8' codec can't decode byte 0xb6 in position 0: invalid start byte, nhưng nếu bạn làm như vậy seek(0); read(2); read(1), bạn sẽ nhận được điều 'a'bạn mong đợi, đó là: seek()không bao giờ được mã hóa -biết, read()là nếu bạn mở tệp ở chế độ văn bản. Bây giờ nếu có 'aöaö' * 1000000, các khối của bạn sẽ không được căn chỉnh chính xác.
norok2

23

Còn những thứ như thế này thì sao:

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

Vì tệp được đọc theo từng ký tự theo thứ tự ngược lại, nên nó sẽ hoạt động ngay cả trên các tệp rất lớn, miễn là các dòng riêng lẻ phù hợp với bộ nhớ.


20

Bạn cũng có thể sử dụng mô-đun python file_read_backwards.

Sau khi cài đặt nó, thông qua pip install file_read_backwards(v1.2.1), bạn có thể đọc toàn bộ tệp ngược (dòng thông minh) theo cách hiệu quả bộ nhớ thông qua:

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

Nó hỗ trợ mã hóa "utf-8", "latin-1" và "ascii".

Hỗ trợ cũng có sẵn cho python3. Tài liệu khác có thể được tìm thấy tại http://file-read-backwards.readthedocs.io/en/latest/readme.html


Cảm ơn giải pháp này. Tôi thích (và cũng nêu lên) giải pháp ở trên bởi @srohde vì nó giúp tôi hiểu cách thức thực hiện, nhưng là một nhà phát triển tôi thích sử dụng một mô-đun hiện có khi tôi có thể, vì vậy tôi rất vui khi biết về giải pháp này.
joanis

1
Điều này hoạt động với mã hóa đa bào như UTF-8. Giải pháp tìm kiếm / đọc không: tìm kiếm () tính theo byte, read () tính bằng ký tự.
Giê

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

Nếu bạn đang dùng linux, bạn có thể sử dụng taclệnh.

$ tac file

2 công thức bạn có thể tìm thấy trong ActiveState tại đâyđây


1
Tôi tự hỏi nếu đảo ngược () tiêu thụ toàn bộ chuỗi trước khi lặp. Tài liệu nói rằng một __reversed__()phương thức là cần thiết, nhưng python2.5 không phàn nàn về một lớp tùy chỉnh mà không có nó.
muhuk

@muhuk, có lẽ nó phải lưu vào bộ đệm bằng cách nào đó, tôi nghi ngờ nó tạo ra một danh sách mới theo thứ tự ngược lại sau đó trả về một trình vòng lặp cho điều đó
Matt Joiner

1
@Matt: đó sẽ là vô lý. Nó chỉ đơn giản là đi từ phía sau ra phía trước-- len (L) -1 là phía sau, 0 là phía trước. Bạn có thể hình dung phần còn lại.
Devin Jeanpierre

@muhuk: Chuỗi không được tiêu thụ một cách có ý nghĩa (bạn có thể lặp lại toàn bộ chuỗi, nhưng nó không quan trọng lắm). Một __reversed__phương pháp cũng không cần thiết, và đã không sử dụng để trở thành một thứ như vậy. Nếu một đối tượng cung cấp __len____getitem__nó sẽ hoạt động tốt (trừ một số trường hợp ngoại lệ, chẳng hạn như dict).
Devin Jeanpierre

@Devin Jeanpierre: Chỉ khi readlines () trả về một đối tượng cung cấp __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)

Điều này dường như tạo ra đầu ra sai cho các tệp lớn hơn bộ đệm. Nó sẽ không xử lý chính xác các dòng trải dài các đoạn có kích thước bộ đệm mà bạn đọc, như tôi hiểu. Tôi đã đăng một câu trả lời tương tự (cho một câu hỏi tương tự khác).
Darius Bacon

@Darius: À đúng, tôi dường như đã bỏ lỡ một chút. Nên sửa ngay.
Ignacio Vazquez-Abrams

Có vẻ đúng. Tôi vẫn thích mã riêng của mình vì mã O (N ^ 2) này hoạt động trên một tệp lớn có tất cả một dòng dài. (Trong các câu trả lời tương tự cho câu hỏi khác mà tôi đã thử nghiệm điều này gây ra sự suy giảm nghiêm trọng chính hãng trên các tập tin như vậy.)
Darius Bacon

3
Chà, câu hỏi không đề cập đến hiệu suất, vì vậy tôi không thể khắc phục thảm họa hiệu suất là biểu thức thông thường: P
Matt Joiner

Một số giải thích thêm sẽ hữu ích như hiệu suất và nếu điều này thực sự có thể tìm cách nói dòng cuối cùng và chỉ đọc đoạn đó.
dùng1767754

7

Câu trả lời được chấp nhận sẽ không hoạt động đối với các trường hợp có tệp lớn không phù hợp với bộ nhớ (đây không phải là trường hợp hiếm).

Như đã được người khác lưu ý, câu trả lời @srohde có vẻ tốt, nhưng nó có vấn đề tiếp theo:

  • mở tệp có vẻ dư thừa, khi chúng ta có thể chuyển đối tượng tệp và để nó cho người dùng quyết định nên đọc mã hóa nào,
  • ngay cả khi chúng tôi tái cấu trúc để chấp nhận đối tượng tệp, nó sẽ không hoạt động cho tất cả các mã hóa: chúng tôi có thể chọn tệp có utf-8nội dung mã hóa và không phải mã ascii như

    й

    vượt qua buf_sizebằng 1và sẽ có

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

    tất nhiên văn bản có thể lớn hơn nhưng buf_sizecó thể được chọn để dẫn đến lỗi bị che khuất như trên,

  • chúng tôi không thể chỉ định dấu phân cách dòng tùy chỉnh,
  • chúng ta không thể chọn giữ dấu phân cách dòng.

Vì vậy, xem xét tất cả những mối quan tâm này, tôi đã viết các chức năng riêng biệt:

  • một hoạt động với các luồng byte,
  • cái thứ hai hoạt động với các luồng văn bản và ủy thác luồng byte bên dưới của nó cho luồng đầu tiên và giải mã các dòng kết quả.

Trước hết hãy xác định các chức năng tiện ích tiếp theo:

ceil_divisionđể thực hiện phân chia với trần nhà (ngược lại với //phân chia tiêu chuẩn với sàn, có thể tìm thấy thêm thông tin trong chủ đề này )

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

split để tách chuỗi bằng dấu phân cách đã cho từ đầu bên phải với khả năng giữ chuỗi:

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 để đọc lô từ cuối bên phải của luồng nhị phân

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)

Sau đó, chúng ta có thể định nghĩa hàm để đọc luồng byte theo thứ tự ngược lại như

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

và cuối cùng, một chức năng đảo ngược tệp văn bản có thể được định nghĩa như:

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

Xét nghiệm

Chuẩn bị

Tôi đã tạo 4 tệp bằng fsutillệnh :

  1. blank.txt không có nội dung, kích thước 0MB
  2. tiny.txt với kích thước 1MB
  3. small.txt với kích thước 10MB
  4. Large.txt với kích thước 50MB

Ngoài ra, tôi đã tái cấu trúc giải pháp @srohde để làm việc với đối tượng tệp thay vì đường dẫn tệp.

Kịch bản kiểm tra

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

Lưu ý : Tôi đã sử dụng collections.dequelớp để xả khí.

Đầu ra

Đối với PyPy 3.5 trên 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

Đối với CPython 3.5 trên 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

Vì vậy, như chúng ta có thể thấy nó thực hiện như giải pháp ban đầu, nhưng tổng quát hơn và không có nhược điểm được liệt kê ở trên.


Quảng cáo

Tôi đã thêm 0.3.0phiên bản này vào phiên bản của lzgói (yêu cầu Python 3.5 +) có nhiều tiện ích chức năng / lặp được kiểm tra tốt.

Có thể được sử dụng như

 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)

Nó hỗ trợ tất cả các mã hóa tiêu chuẩn (có thể ngoại trừ utf-7vì tôi khó xác định chiến lược tạo chuỗi có thể mã hóa với nó).


2

Ở đây bạn có thể tìm thấy triển khai của tôi, bạn có thể giới hạn việc sử dụng ram bằng cách thay đổi biến "bộ đệm", có một lỗi là chương trình in một dòng trống ngay từ đầu.

Và việc sử dụng ram cũng có thể tăng nếu không có dòng mới nào nhiều hơn byte bộ đệm, biến "rò rỉ" sẽ tăng cho đến khi nhìn thấy một dòng mới ("\ n").

Điều này cũng hoạt động đối với các tệp 16 GB lớn hơn tổng bộ nhớ của tôi.

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

Cảm ơn câu trả lời @srohde. Nó có một lỗi nhỏ kiểm tra ký tự dòng mới với toán tử 'là' và tôi không thể nhận xét về câu trả lời với 1 danh tiếng. Ngoài ra, tôi muốn quản lý tệp mở bên ngoài vì điều đó cho phép tôi nhúng các đoạn lan man của mình cho các tác vụ luigi.

Những gì tôi cần thay đổi có dạng:

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

Tôi muốn đổi thành:

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

Đây là một câu trả lời sửa đổi muốn xử lý tệp và giữ dòng mới:

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

một chức năng đơn giản để tạo một tập tin thứ hai đảo ngược (chỉ linux):

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

cách sử dụng

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

Tôi nghĩ mục tiêu là làm thế nào để làm điều đó trong Python. Thêm vào đó, điều này chỉ hoạt động trên các hệ thống * Nix, mặc dù đó là một giải pháp tuyệt vời cho điều đó. Về cơ bản, nó chỉ sử dụng Python như một dấu nhắc để chạy các tiện ích shell.
Alexander Huszagh

1
Mã này có lỗi bảo mật lớn như được viết hiện nay. Điều gì xảy ra nếu bạn đang cố gắng đảo ngược một tệp được tạo bằng mv mycontent.txt $'hello $(rm -rf $HOME) world.txt'hoặc tương tự bằng cách sử dụng tên tệp đầu ra do người dùng không tin cậy cung cấp? Nếu bạn muốn xử lý tên tập tin tùy ý một cách an toàn, cần thận trọng hơn. subprocess.Popen(['tac', file1], stdout=open(file2, 'w'))sẽ được an toàn, ví dụ.
Charles Duffy

Mã hiện tại cũng không xử lý chính xác các tệp có dấu cách, ký tự đại diện, & c.
Charles Duffy


1

với mở ("tên tệp") là f:

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

Điều này có đọc toàn bộ tập tin trong? Điều này có an toàn trên các tập tin lớn? Đây có vẻ là một cách rất dễ dàng và thực tế để làm điều đó nhưng không chắc chắn về các câu hỏi trên .. Tôi muốn tìm kiếm tệp theo cách này (sử dụng lại) ..
ikwyl6

@ ikwyl6 Điều này nên tương đương với list(reversed(f.read())).
AMC


0

Luôn sử dụng withkhi làm việc với các tệp vì nó xử lý mọi thứ cho bạn:

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

Hoặc trong Python 3:

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

0

trước tiên bạn cần mở tệp của mình ở định dạng đọc, lưu nó vào một biến, sau đó mở tệp thứ hai ở định dạng ghi nơi bạn sẽ viết hoặc nối thêm biến bằng cách sử dụng lát [:: - 1], đảo ngược hoàn toàn tệp. Bạn cũng có thể sử dụng readlines () để biến nó thành một danh sách các dòng mà bạn có thể thao tác

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

Hầu hết các câu trả lời cần phải đọc toàn bộ tập tin trước khi làm bất cứ điều gì. Mẫu này đọc các mẫu ngày càng lớn từ cuối .

Tôi chỉ thấy câu trả lời của Murat Yükselen trong khi viết câu trả lời này. Nó gần giống nhau, mà tôi cho là một điều tốt. Mẫu bên dưới cũng xử lý \ r và tăng bộ đệm của nó ở mỗi bước. Tôi cũng có một số bài kiểm tra đơn vị để sao lưu mã này.

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

Đọc từng dòng tệp và sau đó thêm nó vào danh sách theo thứ tự ngược lại.

Đây là một ví dụ về mã:

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

Đây chỉ là một phiên bản kém hơn của giải pháp trong câu trả lời được chấp nhận .
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

sử dụng:

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

-3

Tôi đã phải làm điều này một thời gian trước và sử dụng mã dưới đây. Nó ống đến vỏ. Tôi sợ rằng tôi không có kịch bản hoàn chỉnh nữa. Nếu bạn đang sử dụng hệ điều hành unixish, bạn có thể sử dụng "tac", tuy nhiên, ví dụ: lệnh Mac OSX tac không hoạt động, hãy sử dụng đuôi -r. Đoạn mã dưới đây kiểm tra xem bạn đang sử dụng nền tảng nào và điều chỉnh lệnh cho phù hợp

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

Các poster đang tìm kiếm một câu trả lời python.
mikemaccana

Vâng, đó là một câu trả lời Python mặc dù nó dường như không đầy đủ.
DrDee

2
Nó không, không phải nền tảng chéo, sử dụng các lệnh hệ thống = không phải pythonic
Phyo Arkar Lwin

Người đăng đang tìm kiếm một câu trả lời "sử dụng python", mà đoạn mã thực sự được viết. Nhưng tôi đồng ý rằng đó không phải là một giải pháp rất tốt so với nhiều người khác được đăng.
jeorgen

1
Đoạn mã không đủ để đánh giá tính chính xác (các phần khác của lệnh gọi không được hiển thị), nhưng việc lưu trữ các lệnh shell trong chuỗi là rất đáng ngờ - rất dễ xảy ra lỗi tiêm vỏ trừ khi thực hiện rất nhiều lỗi Chăm sóc.
Charles Duffy
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.