我建议您在解析之前将文件分成大小相似的块。
下面的示例代码将通过查找和搜索下一个换行符将data.csv 分成大小大致相等的 4 块。然后它会为每个块调用launch_worker(),指明worker应该处理的数据的起始偏移量和长度。
理想情况下,您应该为每个工作人员使用 subprocess。
import os
n_workers = 4
# open the log file, and find out how long it is
f = open('data.csv', 'rb')
length_total = f.seek(0, os.SEEK_END)
# split the file evenly among n workers
length_worker = int(length_total / n_workers)
prev_worker_end = 0
for i in range(n_workers):
# seek to the next worker's approximate start
file_pos = f.seek(prev_worker_end + length_worker, os.SEEK_SET)
# see if we tried to seek past the end of the file... the last worker probably will
if file_pos >= length_total: # <-- (3)
# ... if so, this worker's chunk extends to the end of the file
this_worker_end = length_total
else:
# ... otherwise, look for the next line break
buf = f.read(256) # <-- (1)
next_line_end = buf.index(b'\n') # <-- (2)
this_worker_end = file_pos + next_line_end
# calculate how long this worker's chunk is
this_worker_length = this_worker_end - prev_worker_end
if this_worker_length > 0:
# if there is any data in the chunk, then try to launch a worker
launch_worker(prev_worker_end, this_worker_length)
# remember where the last worker got to in the file
prev_worker_end = this_worker_end + 1
代码中标记的一些扩展:
- 您需要确保
read() 至少会占用一整行。或者,如果您不知道一行可以提前多长时间,您可以循环执行多个 read()s。
- 这假定
\n 行结尾...您可能需要针对您的数据进行修改。
- 最后一个工作人员处理的数据比其他工作人员要少...这是因为我们总是向前搜索下一个换行符。你拥有的工人越多,最终工人获得的数据就越少。这不是很重要(在我的测试中约为 200-500 字节)。
确保您始终使用二进制模式,因为文本模式会给您带来不稳定的seek()s / read()s。
launch_worker() 的示例如下所示:
def launch_worker(offset, length):
print('Starting a worker... using chunk %d - %d (%d bytes)...'
% ( offset, offset + length, length ))
with open('log.txt', 'rb') as f:
f.seek(offset, os.SEEK_SET)
worker_buf = f.read(length)
lines = worker_buf.split(b'\n')
print('First Line:')
print('\t' + str(lines[0]))
print('Last Line:')
print('\t' + str(lines[-1]))