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?
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 osdef 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 readif segment isnotNone:# 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]+= segmentelse: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 emptyif segment isnotNone:yield segment
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 -=1yield 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.
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.7from file_read_backwards importFileReadBackwardswithFileReadBackwards("/path/to/file", encoding="utf-8")as frb:for l in frb:print l
Mendukung pengkodean "utf-8", "latin-1", dan "ascii".
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
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)-2while 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)
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
Saya telah menghasilkan 4 file menggunakan fsutilperintah :
empty.txt tanpa isi, ukuran 0MB
tiny.txt dengan ukuran 1MB
small.txt dengan ukuran 10MB
large.txt dengan ukuran 50MB
juga saya solusi refactored @srohde untuk bekerja dengan objek file, bukan path file.
Skrip uji
from timeit importTimer
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).
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 lineif division - chunk_counter <0:print line_leak
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 lineprint'>{}<'.format(line)
Saya ingin mengubah ke:
with open(filename)as fp:for line in reversed_fp_iter(fp,4):#print line, # contains new lineprint'>{}<'.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 readif segment isnotNone:# 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 firstif 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 emptyif segment isnotNone:yield segment
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
Jika Anda khawatir tentang ukuran file / penggunaan memori, pemetaan memori file dan pemindaian mundur untuk baris baru adalah solusi:
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())).
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
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,4096whileTrue: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 breaksifnot lines[-1].endswith((b"\n", b"\r")):
lines[-1]+= head # current tail is previous headelif head == b"\n"and lines[-1].endswith(b"\r"):
lines[-1]+= head # Keep \r\n togetherelif head:
lines.append(head)
head = lines.pop(0)# can be '\n' (ok)# Iterate over current block in reversefor line in reversed(lines):yield line
if head:yield head
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:raiseEnvironmentError('Platform %s not supported'% sys.platform)
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.
Jawaban:
Dan dengan Python 3:
sumber
with
Pernyataan biasanya cukup menyakitkan.Jawaban yang benar dan efisien ditulis sebagai generator.
sumber
fh.seek(0, os.SEEK_END)
dan mengubahfh.seek(-offset, os.SEEK_END)
jugafh.seek(file_size - offset)
.fh.seek()
pengembalianNone
utf8
),seek()
danread()
merujuk ke ukuran yang berbeda. Itu mungkin juga alasan mengapa argumen pertama non-nolseek()
relatifos.SEEK_END
tidak didukung.'aöaö'.encode()
adalahb'a\xc3\xb6a\xc3\xb6'
. Jika Anda menyimpan ini ke disk dan kemudian membaca dalam mode teks, ketika Anda melakukannyaseek(2)
akan bergerak dua byte, sehinggaseek(2); read(1)
akan menghasilkan kesalahanUnicodeDecodeError: 'utf-8' codec can't decode byte 0xb6 in position 0: invalid start byte
, tetapi jika Anda melakukannyaseek(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.Bagaimana dengan sesuatu yang seperti ini:
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.
sumber
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: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
sumber
Jika Anda menggunakan linux, Anda dapat menggunakan
tac
perintah.2 resep yang dapat Anda temukan di ActiveState di sini dan di sini
sumber
__reversed__()
metode diperlukan, tetapi python2.5 tidak mengeluh pada kelas khusus tanpa itu.__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).__reversed__
?sumber
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:
bahkan jika kita menolak untuk menerima objek file, itu tidak akan berfungsi untuk semua penyandian: kita dapat memilih file dengan
utf-8
penyandian dan konten non-ascii sepertilulus
buf_size
sama dengan1
dan akan dimilikitentu saja teks mungkin lebih besar tetapi
buf_size
dapat diambil sehingga akan menyebabkan kesalahan yang dikaburkan seperti di atas,Jadi mempertimbangkan semua masalah ini saya telah menulis fungsi terpisah:
Pertama-tama mari kita mendefinisikan fungsi utilitas selanjutnya:
ceil_division
untuk membuat pembagian dengan langit-langit (berbeda dengan//
divisi standar dengan lantai, info lebih lanjut dapat ditemukan di utas ini )split
untuk memisahkan string dengan memberikan pemisah dari ujung kanan dengan kemampuan untuk menyimpannya:read_batch_from_end
untuk membaca kumpulan dari ujung kanan aliran binerSetelah itu kita dapat mendefinisikan fungsi untuk membaca aliran byte dalam urutan terbalik seperti
dan akhirnya fungsi untuk membalikkan file teks dapat didefinisikan seperti:
Tes
Persiapan
Saya telah menghasilkan 4 file menggunakan
fsutil
perintah :juga saya solusi refactored @srohde untuk bekerja dengan objek file, bukan path file.
Skrip uji
Catatan : Saya sudah menggunakan
collections.deque
class untuk knalpot generator.Keluaran
Untuk PyPy 3.5 pada Windows 10:
Untuk CPython 3.5 di Windows 10:
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.0
versilz
paket (membutuhkan Python 3.5 +) yang memiliki banyak utilitas fungsional / iterasi yang teruji.Dapat digunakan seperti
Ini mendukung semua pengkodean standar (mungkin kecuali
utf-7
karena sulit bagi saya untuk menentukan strategi untuk menghasilkan string yang dikodekan dengannya).sumber
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.
sumber
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:
Saya ingin mengubah ke:
Berikut ini adalah jawaban yang dimodifikasi yang menginginkan file menangani dan membuat baris baru:
sumber
fungsi sederhana untuk membuat file kedua terbalik (hanya linux):
Cara Penggunaan
sumber
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.Jika Anda khawatir tentang ukuran file / penggunaan memori, pemetaan memori file dan pemindaian mundur untuk baris baru adalah solusi:
Bagaimana cara mencari string dalam file teks?
sumber
dengan open ("nama file") sebagai f:
sumber
list(reversed(f.read()))
.sumber
Selalu gunakan
with
ketika bekerja dengan file karena menangani segalanya untuk Anda:Atau dengan Python 3:
sumber
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
sumber
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.
sumber
Baca file baris demi baris dan kemudian tambahkan pada daftar dalam urutan terbalik.
Berikut ini contoh kode:
sumber
sumber
menggunakan:
sumber
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
sumber