Как получить время, необходимое для распаковки больших файлов bz2?

Мне нужно обрабатывать большие файлы bz2 (~6G), используя Python, распаковывая его построчно, используя BZ2File.readline(), Проблема в том, что я хочу знать, сколько времени нужно для обработки всего файла.

Я провел много поисков, попытался определить фактический размер распакованного файла, чтобы я мог узнать процент обработанного на лету и, следовательно, оставшееся время, в то время как обнаружил, что невозможно узнать размер распакованного файла. без предварительной распаковки ( /questions/27683924/razmer-neszhatogo-fajla-python-bz2/27683936#27683936).

Помимо того, что распаковка файла занимает много памяти, распаковка занимает много времени. Итак, кто-нибудь может помочь мне получить оставшееся время обработки на лету?

3 ответа

Решение

Вы можете оценить оставшееся время на основе потребления сжатых данных вместо производства несжатых данных. Результат будет примерно таким же, если данные будут относительно однородными. (Если это не так, то использование входных данных или выходных данных в любом случае не даст точной оценки.)

Вы можете легко найти размер сжатого файла и использовать время, потраченное на сжатые данные, чтобы оценить время для обработки оставшихся сжатых данных.

Вот простой пример использования 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')

Можно использовать непосредственно существующие высокоуровневые 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
    ...

С помощью другого ответа, наконец, я нашел решение. Идея состоит в том, чтобы использовать размер обработанного сжатого файла, общий размер сжатого файла и время, используемое для оценки оставшегося времени. Для достижения этой цели,

  1. прочитать сжатый файл как объект байта в память: byte_data, что довольно быстро
  2. рассчитать размер byte_data с помощью total_size = len(byte_data)
  3. заворачивать byte_data как byte_f = io.BytesIO(byte_data)
  4. заворачивать byte_f как bz2f = bz2.BZ2File(byte_f)
  5. во время обработки используйте pos = byte_f.tell() получить текущую позицию в сжатом файле
  6. рассчитать точный процент обработки percent = pos/total_size
  7. записать использованное время и рассчитать оставшееся время

Через несколько секунд оценка может стать довольно точной:

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...
Другие вопросы по тегам