Мне нужно обрабатывать большие файлы bz2 (~ 6G) с помощью Python, распаковывая их построчно, используя BZ2File.readline(). Проблема в том, что я хочу знать, сколько времени нужно для обработки всего файла.
Я сделал много поисков, попытался получить фактический размер распакованного файла, чтобы я мог знать процент, обработанный на лету, и, следовательно, оставшееся время, в то время как вывод состоит в том, что невозможно узнать размер распакованного файла. без предварительной распаковки (https://stackoverflow.com/a/12647847/7876675).
Помимо того, что распаковка файла занимает много памяти, сама распаковка занимает много времени. Итак, может ли кто-нибудь помочь мне получить оставшееся время обработки на лету?
Используя модуль bz2, вы сможете открыть его в python как поток и прочитать каждую строку. это сэкономит вам память. Что касается того, сколько времени это займет, это во многом зависит от того, что для вас значит «процесс».
@quamrana Боюсь, нет, как указано в ссылке, которую я дал выше.
@owen79 Давайте возьмем самый простой случай: я могу записать время для всех строк, уже прочитанных readline(), но меня больше волнует, сколько времени осталось, чтобы закончить этот файл.
Возможно, вы сможете вызвать tell() для файлового объекта, переданного BZ2File , и сравнить текущую позицию с размером всего файла, что даст вам оценку того, сколько было обработано. Этой информации плюс знание того, сколько времени уже прошло, будет достаточно, чтобы предсказать, сколько времени это займет.
@martineau tell() дает вам позицию в файле декомпрессия, но опять же, вам нужно знать исходный размер файла, чтобы оценить процент обработки.
Вызов tell() для базового (сжатого) файла, который BZ2File читает (пока он находится в процессе чтения), даст вам представление о том, какая часть файла была обработана.
@martineau Спасибо за объяснение. Вы правы насчет tell(), но, не зная оставшегося размера файла несжатый, оценить оставшееся время все равно невозможно.
Из вашего последнего комментария я боюсь, что вы все еще не понимаете. Не обязательно знать оставшийся размер, достаточно знать общий размер. Позвонив в tell(), вы узнаете, какая часть этой суммы была обработана BZ2File в любой момент времени. Например, предположим, что 25%, если общий размер файла был прочитан до сих пор, и это заняло 5 секунд. Это позволит вам оценить, что чтение оставшихся 75% займет в 4 раза больше времени: т.е. (4 * 5) = 20 секунд.
Как я упоминал в описании вопроса, невозможно узнать общий размер (или обработанный процент) несжатого файла без предварительной распаковки всего файла. @martineau спасибо за попытку помочь.
Эх... чтобы сделать то, что я предлагаю, вам нужно знать только общий размер файла сжатый.
Хорошо, здесь много недоразумений. Две вещи я могу получить точно: размер файла сжатый и положение в файле несжатый с помощью tell(). Если вы, @martineau, предлагаете использовать tell() для оценки обработанного процента файла сжатый, я должен с уважением не согласиться, потому что сжатый файл обычно в несколько раз меньше несжатого файла, а это означает, что процент скоро превысит 100%. Спасибо за ваши советы, но они не решили мою проблему.
Да, я предлагаю вам использовать процент обработанного сжатого файла, чтобы оценить, сколько времени потребуется, чтобы распаковать оставшуюся часть. Конечно, это будет не совсем точно, поэтому он называется оценить.






Вы можете оценить оставшееся время на основе потребление данных сжатый, а не производство данных несжатый. Результат будет примерно таким же, если данные относительно однородны. (Если это не так, то использование входных или выходных данных все равно не даст точной оценки.)
Вы можете легко определить размер сжатого файла и использовать время, затраченное на сжатые данные, чтобы оценить время обработки оставшихся сжатых данных.
Вот простой пример использования объекта BZ2Decompress для обработки входных данных по частям за раз, показывающий ход чтения (Python 3, получение имени файла из командной строки):
# Decompress a bzip2 file, showing progress based on consumed input.
import sys
import os
import bz2
import time
def proc(input):
"""Decompress and process a piece of a compressed stream"""
dat = dec.decompress(input)
got = len(dat)
if got != 0: # 0 is common -- waiting for a bzip2 block
# process dat here
pass
return got
# Get the size of the compressed bzip2 file.
path = sys.argv[1]
size = os.path.getsize(path)
# Decompress CHUNK bytes at a time.
CHUNK = 16384
totin = 0
totout = 0
prev = -1
dec = bz2.BZ2Decompressor()
start = time.time()
with open(path, 'rb') as f:
for chunk in iter(lambda: f.read(CHUNK), b''):
# feed chunk to decompressor
got = proc(chunk)
# handle case of concatenated bz2 streams
if dec.eof:
rem = dec.unused_data
dec = bz2.BZ2Decompressor()
got += proc(rem)
# show progress
totin += len(chunk)
totout += got
if got != 0: # only if a bzip2 block emitted
frac = round(1000 * totin / size)
if frac != prev:
left = (size / totin - 1) * (time.time() - start)
print(f'\r{frac / 10:.1f}% (~{left:.1f}s left) ', end='')
prev = frac
# Show the resulting size.
print(end='\r')
print(totout, 'uncompressed bytes')
Да, я могу получить размер всего сжатого файла, но есть идеи, как получить потребляемый размер?
Используйте BZ2Decompressor и загружайте его только порциями. Тогда потребляемый размер - это то, что вы кормили до сих пор.
С помощью другого ответа, наконец, я нашел решение. Идея состоит в том, чтобы использовать размер обрабатываемого сжатого файла, общий размер сжатого файла и время, используемое для оценки оставшегося времени. Для достижения этой цели,
byte_data, что довольно быстроbyte_data, используя total_size = len(byte_data)byte_data как byte_f = io.BytesIO(byte_data)byte_f как bz2f = bz2.BZ2File(byte_f)pos = byte_f.tell() для получения текущей позиции в файле сжатыйpercent = pos/total_sizeЧерез несколько секунд оценка может стать довольно точной:
0.01% processed, 2.00s elapsed, 17514.27s remaining...
0.02% processed, 4.00s elapsed, 20167.48s remaining...
0.03% processed, 6.00s elapsed, 21239.60s remaining...
0.04% processed, 8.00s elapsed, 21818.91s remaining...
0.05% processed, 10.00s elapsed, 22180.76s remaining...
0.05% processed, 12.00s elapsed, 22427.78s remaining...
0.06% processed, 14.00s elapsed, 22661.80s remaining...
0.07% processed, 16.00s elapsed, 22840.45s remaining...
0.08% processed, 18.00s elapsed, 22937.07s remaining...
....
99.97% processed, 22704.28s elapsed, 6.27s remaining...
99.98% processed, 22706.28s elapsed, 4.40s remaining...
99.99% processed, 22708.28s elapsed, 2.45s remaining...
100.00% processed, 22710.28s elapsed, 0.54s remaining...
Вам не нужно загружать все это в память. Это займет ненужные ресурсы для больших файлов. Используйте объект BZ2Decompressor вместо BZ2File и считывайте фрагменты за раз, создавая фрагменты вывода за раз, обрабатывая вывод на ходу.
@MarkAdler Думаю, ты прав. Но в моей ситуации чтение всего файла в памяти ускорит работу программы, а использование BZ2File.readline() может значительно упростить обработку.
Можно использовать непосредственно существующие высокоуровневые API, предоставляемые модулем bz2 Python, и в то же время получать информацию от базового обработчика файлов о том, сколько сжатых данных было обработано.
import bz2
import datetime
import time
with bz2.open(input_filename, 'rt', encoding='utf8') as input_file:
underlying_file = input_file.buffer._buffer.raw._fp
underlying_file.seek(0, io.SEEK_END)
underlying_file_size = underlying_file.tell()
underlying_file.seek(0, io.SEEK_SET)
lines_count = 0
start_time = time.perf_counter()
progress = f'{0:.2f}%'
while True:
line = input_file.readline().strip()
if not line:
break
process_line(line)
lines_count += 1
current_position = underlying_file.tell()
new_progress = f'{current_position / underlying_file_size * 100:.2f}%'
if progress != new_progress:
progress = new_progress
current_time = time.perf_counter()
elapsed_time = current_time - start_time
elapsed = datetime.timedelta(seconds=elapsed_time)
remaining = datetime.timedelta(seconds=(underlying_file_size / current_position - 1) * elapsed_time)
print(f"{lines_count} lines processed, {progress}, {elapsed} elapsed, {remaining} remaining")
Если вы читаете не текстовые файлы, а двоичные файлы, вам нужно использовать:
with bz2.open(input_filename, 'rb') as input_file:
underlying_file = input_file._buffer.raw._fp
...
Есть ли в любом случае доступный для вас распакованный размер? Если это файлы, то кто-то должен знать, насколько велики были файлы в первую очередь. Тогда вы сможете угадать, сколько времени займет распаковка, по тому, сколько времени это заняло.