Python của seek
đi đến một byte bù đắp trong một tập tin, không một dòng bù đắp, đơn giản chỉ vì đó là cách các hệ điều hành hiện đại và hệ thống tập tin của họ hoạt động - hệ điều hành/FS chỉ không ghi hoặc ghi nhớ "bù đắp" theo bất kỳ cách nào, và không có cách nào cho Python (hoặc bất kỳ ngôn ngữ nào khác) để chỉ kỳ diệu đoán chúng. Bất kỳ hoạt động nào nhằm mục đích "đi đến một dòng" chắc chắn sẽ cần phải "đi qua các tập tin" (dưới bìa) để làm cho sự kết hợp giữa số dòng và bù đắp byte.
Nếu bạn đồng ý với điều đó và chỉ muốn ẩn nó khỏi tầm nhìn của bạn, thì giải pháp là mô-đun thư viện chuẩn linecache - nhưng hiệu suất sẽ không tốt hơn mã bạn có thể tự viết.
Nếu bạn cần đọc từ cùng một tệp lớn nhiều lần, tối ưu hóa lớn sẽ chạy một lần trên tệp lớn mà tập lệnh tạo và lưu vào đĩa tương ứng với số dòng từ byte (kỹ thuật) tệp phụ "chỉ mục"); sau đó, tất cả các lần chạy liên tiếp của bạn (cho đến khi các tệp lớn thay đổi) có thể nhanh chóng sử dụng tệp chỉ mục để điều hướng với hiệu suất rất cao thông qua tệp lớn. Đây có phải là trường hợp sử dụng của bạn ...?
Chỉnh sửa: vì dường như điều này có thể áp dụng - đây là ý tưởng chung (mạng kiểm tra cẩn thận, kiểm tra lỗi hoặc tối ưu hóa ;-).Để thực hiện các chỉ số, sử dụng makeindex.py
, như sau:
import array
import sys
BLOCKSIZE = 1024 * 1024
def reader(f):
blockstart = 0
while True:
block = f.read(BLOCKSIZE)
if not block: break
inblock = 0
while True:
nextnl = block.find(b'\n', inblock)
if nextnl < 0:
blockstart += len(block)
break
yield nextnl + blockstart
inblock = nextnl + 1
def doindex(fn):
with open(fn, 'rb') as f:
# result format: x[0] is tot # of lines,
# x[N] is byte offset of END of line N (1+)
result = array.array('L', [0])
result.extend(reader(f))
result[0] = len(result) - 1
return result
def main():
for fn in sys.argv[1:]:
index = doindex(fn)
with open(fn + '.indx', 'wb') as p:
print('File', fn, 'has', index[0], 'lines')
index.tofile(p)
main()
và sau đó sử dụng nó, ví dụ, sau đây useindex.py
:
import array
import sys
def readline(n, f, findex):
f.seek(findex[n] + 1)
bytes = f.read(findex[n+1] - findex[n])
return bytes.decode('utf8')
def main():
fn = sys.argv[1]
with open(fn + '.indx', 'rb') as f:
findex = array.array('l')
findex.fromfile(f, 1)
findex.fromfile(f, findex[0])
findex[0] = -1
with open(fn, 'rb') as f:
for n in sys.argv[2:]:
print(n, repr(readline(int(n), f, findex)))
main()
Dưới đây là một ví dụ (trên máy tính xách tay chậm của tôi):
$ time py3 makeindex.py kjv10.txt
File kjv10.txt has 100117 lines
real 0m0.235s
user 0m0.184s
sys 0m0.035s
$ time py3 useindex.py kjv10.txt 12345 98765 33448
12345 '\r\n'
98765 '2:6 But this thou hast, that thou hatest the deeds of the\r\n'
33448 'the priest appointed officers over the house of the LORD.\r\n'
real 0m0.049s
user 0m0.028s
sys 0m0.020s
$
Tệp mẫu là một tệp văn bản thuần túy của King James 'Kinh thánh:
$ wc kjv10.txt
100117 823156 4445260 kjv10.txt
100K dòng, 4,4 MB, như bạn có thể thấy; điều này mất khoảng một phần tư giây để lập chỉ mục và 50 mili giây để đọc và in ra ba dòng ngẫu nhiên (không nghi ngờ điều này có thể được tăng tốc rất nhiều với tối ưu hóa cẩn thận hơn và máy tốt hơn). Chỉ số trong bộ nhớ (và trên đĩa quá) mất 4 byte trên mỗi dòng của textfile được lập chỉ mục, và hiệu suất nên quy mô một cách hoàn hảo tuyến tính, vì vậy nếu bạn có khoảng 100 triệu dòng, 4.4 GB, tôi mong đợi khoảng 4-5 phút để xây dựng chỉ mục, một phút để trích xuất và in ra ba dòng tùy ý (và 400 MB RAM được lấy cho chỉ mục không nên bất tiện ngay cả một máy nhỏ - ngay cả máy tính xách tay chậm chạp của tôi có 2GB sau tất cả ;-). Bạn cũng có thể thấy rằng (đối với tốc độ và tiện lợi), tôi coi tệp là nhị phân (và giả sử mã hóa utf8 - hoạt động với bất kỳ tập con nào như ASCII, ví dụ: tệp văn bản KJ là ASCII) và không bận tâm thu hẹp \r\n
thành một ký tự đơn nếu đó là những gì tệp có dạng trình kết thúc dòng (nó khá tầm thường để làm điều đó sau khi đọc từng dòng nếu bạn muốn).
tôi sẽ rất vui nếu có giải pháp cho trường hợp khi tôi có cùng số lượng số nguyên trên mỗi tệp trong tệp. –