Để đọc cả dòng đầu tiên và cuối cùng của một tệp bạn có thể ...
- Mở tập tin, ...
- ... Đọc dòng đầu tiên bằng cách sử dụng
readline[]
tích hợp, ... - ... Tìm kiếm [di chuyển con trỏ] đến cuối tệp, ...
- ... Bước lùi cho đến khi bạn gặp EOL [Line Break] và ...
- ... Đọc dòng cuối cùng từ đó.
def readlastline[f]:
f.seek[-2, 2] # Jump to the second last byte.
while f.read[1] != b"\n": # Until EOL is found ...
f.seek[-2, 1] # ... jump back, over the read byte plus one more.
return f.read[] # Read all data from this point on.
with open[file, "rb"] as f:
first = f.readline[]
last = readlastline[f]
Nhảy đến byte cuối cùng thứ hai trực tiếp để ngăn các ký tự dòng mới để khiến các dòng trống được trả về*.
Phần bù hiện tại được đẩy về phía trước mỗi lần một byte được đọc để bước lùi được thực hiện hai byte cùng một lúc, qua byte đọc gần đây và byte để đọc tiếp theo.
Tham số whence
được chuyển đến fseek[offset, whence=0]
chỉ ra rằng fseek
sẽ tìm kiếm một vị trí offset
byte so với ...
0
hoặc
0 = phần đầu của tệp.10k iterations processing a file of 6k lines totalling 200kB: 1.62s vs 6.92s. 100 iterations processing a file of 6k lines totalling 1.3GB: 8.93s vs 86.95.
1 hoặc10k iterations processing a file of 6k lines totalling 200kB: 1.62s vs 6.92s. 100 iterations processing a file of 6k lines totalling 1.3GB: 8.93s vs 86.95.
2 = vị trí hiện tại.10k iterations processing a file of 6k lines totalling 200kB: 1.62s vs 6.92s. 100 iterations processing a file of 6k lines totalling 1.3GB: 8.93s vs 86.95.
3 hoặc10k iterations processing a file of 6k lines totalling 200kB: 1.62s vs 6.92s. 100 iterations processing a file of 6k lines totalling 1.3GB: 8.93s vs 86.95.
4 = phần cuối của tệp.10k iterations processing a file of 6k lines totalling 200kB: 1.62s vs 6.92s. 100 iterations processing a file of 6k lines totalling 1.3GB: 8.93s vs 86.95.
* Như mong đợi là hành vi mặc định của hầu hết các ứng dụng, bao gồm
10k iterations processing a file of 6k lines totalling 200kB: 1.62s vs 6.92s.
100 iterations processing a file of 6k lines totalling 1.3GB: 8.93s vs 86.95.
5 và 10k iterations processing a file of 6k lines totalling 200kB: 1.62s vs 6.92s.
100 iterations processing a file of 6k lines totalling 1.3GB: 8.93s vs 86.95.
6, là nối một dòng vào mỗi dòng được viết và không có tác dụng đối với các dòng bị thiếu ký tự dòng mới. Hiệu quả
1-2 triệu dòng mỗi dòng và tôi phải làm điều này cho hàng trăm tệp.
Tôi đã tính thời gian phương pháp này và so sánh nó với câu trả lời hàng đầu.
10k iterations processing a file of 6k lines totalling 200kB: 1.62s vs 6.92s.
100 iterations processing a file of 6k lines totalling 1.3GB: 8.93s vs 86.95.
Hàng triệu dòng sẽ tăng sự khác biệt hơn rất nhiều.
Mã exakt được sử dụng cho thời gian:
with open[file, "rb"] as f:
first = f.readline[] # Read and store the first line.
for last in f: pass # Read all lines, keep final value.
Sửa đổiAmendment
Một biến thể phức tạp hơn và khó đọc hơn, để giải quyết các bình luận và vấn đề được nêu ra kể từ đó.
- Trả về chuỗi trống khi phân tích tệp trống, được nâng lên bởi bình luận.
- Trả lại tất cả nội dung khi không tìm thấy dấu phân cách, được đưa ra bởi bình luận.
- Tránh bù đắp tương đối để hỗ trợ chế độ văn bản, được đưa ra bởi bình luận.
- Hack UTF16/UTF32, được ghi nhận bởi bình luận.
Cũng thêm hỗ trợ cho các phân định đa cấp,
10k iterations processing a file of 6k lines totalling 200kB: 1.62s vs 6.92s.
100 iterations processing a file of 6k lines totalling 1.3GB: 8.93s vs 86.95.
7.Xin lưu ý rằng biến thể này thực sự chậm đối với các tệp lớn vì các lần bù không liên quan cần thiết trong chế độ văn bản. Sửa đổi theo nhu cầu của bạn hoặc hoàn toàn không sử dụng nó vì có lẽ bạn nên sử dụng
8 với các tệp được mở ở chế độ văn bản.10k iterations processing a file of 6k lines totalling 200kB: 1.62s vs 6.92s.
100 iterations processing a file of 6k lines totalling 1.3GB: 8.93s vs 86.95.
#!/bin/python3
from os import SEEK_END
def readlast[f, sep, fixed=True]:
r"""Read the last segment from a file-like object.
:param f: File to read last line from.
:type f: file-like object
:param sep: Segment separator [delimiter].
:type sep: bytes, str
:param fixed: Treat data in ``f`` as a chain of fixed size blocks.
:type fixed: bool
:returns: Last line of file.
:rtype: bytes, str
"""
bs = len[sep]
step = bs if fixed else 1
if not bs:
raise ValueError["Zero-length separator."]
try:
o = f.seek[0, SEEK_END]
o = f.seek[o-bs-step] # - Ignore trailing delimiter 'sep'.
while f.read[bs] != sep: # - Until reaching 'sep': Read sep-sized block
o = f.seek[o-step] # and then seek to the block to read next.
except [OSError,ValueError]: # - Beginning of file reached.
f.seek[0]
return f.read[]
def test_readlast[]:
from io import BytesIO, StringIO
# Text mode.
f = StringIO["first\nlast\n"]
assert readlast[f, "\n"] == "last\n"
# Bytes.
f = BytesIO[b'first|last']
assert readlast[f, b'|'] == b'last'
# Bytes, UTF-8.
f = BytesIO["X\nY\n".encode["utf-8"]]
assert readlast[f, b'\n'].decode[] == "Y\n"
# Bytes, UTF-16.
f = BytesIO["X\nY\n".encode["utf-16"]]
assert readlast[f, b'\n\x00'].decode['utf-16'] == "Y\n"
# Bytes, UTF-32.
f = BytesIO["X\nY\n".encode["utf-32"]]
assert readlast[f, b'\n\x00\x00\x00'].decode['utf-32'] == "Y\n"
# Multichar delimiter.
f = StringIO["X
Y"]
assert readlast[f, "
", fixed=False] == "Y"
# Make sure you use the correct delimiters.
seps = { 'utf8': b'\n', 'utf16': b'\n\x00', 'utf32': b'\n\x00\x00\x00' }
assert "\n".encode['utf8' ] == seps['utf8']
assert "\n".encode['utf16'][2:] == seps['utf16']
assert "\n".encode['utf32'][4:] == seps['utf32']
# Edge cases.
edges = [
# Text , Match
["" , "" ], # Empty file, empty string.
["X" , "X" ], # No delimiter, full content.
["\n" , "\n"],
["\n\n", "\n"],
# UTF16/32 encoded U+270A [b"\n\x00\n'\n\x00"/utf16]
[b'\n\xe2\x9c\x8a\n'.decode[], b'\xe2\x9c\x8a\n'.decode[]],
]
for txt, match in edges:
for enc,sep in seps.items[]:
assert readlast[BytesIO[txt.encode[enc]], sep].decode[enc] == match
if __name__ == "__main__":
import sys
for path in sys.argv[1:]:
with open[path] as f:
print[f.readline[] , end=""]
print[readlast[f,"\n"], end=""]