(Python) Подсчитывание строк в огромном (> 10 ГБ) файле как можно быстрее

У меня сейчас очень простой script, который подсчитывает строки в текстовом файле с помощью enumerate():

i = 0
f = open("C:/Users/guest/Desktop/file.log", "r")
for i, line in enumerate(f):
      pass
print i + 1
f.close()

Это займет около трех с половиной минут, чтобы просмотреть файл журнала размером 15 ГБ с ~ 30 миллионами строк. Было бы здорово, если бы я мог получить это менее двух минут или меньше, потому что это ежедневные журналы, и мы хотим провести ежемесячный анализ, поэтому код должен обработать 30 журналов ~ 15 ГБ - более полутора часов, возможно, и мы хотели бы свести к минимуму время и нагрузку на память на сервере.

Я бы также согласился на хороший метод приближения/оценки, но он должен быть примерно 4 сиг-фига точной...

Спасибо!

Ответ 1

Ответ Ignacio является правильным, но может завершиться неудачно, если у вас есть 32-битный процесс.

Но, может быть, было бы полезно прочитать файл по блоку, а затем подсчитать символы \n в каждом блоке.

def blocks(files, size=65536):
    while True:
        b = files.read(size)
        if not b: break
        yield b

with open("file", "r") as f:
    print sum(bl.count("\n") for bl in blocks(f))

выполнит вашу работу.

Обратите внимание, что я не открываю файл как двоичный, поэтому \r\n будет преобразован в \n, сделав подсчет более надежным.

Ответ 2

Я знаю его немного несправедливо, но вы можете это сделать

int(subprocess.check_output("wc -l C:\\alarm.bat").split()[0])

Если вы на окнах Coreutils

Ответ 3

mmap файл и подсчитайте новые строки.

Ответ 4

Я бы добавил gl ответ и запустил его/ее код, используя многопроцессорный модуль Python для более быстрого подсчета:

def blocks(f, cut, size=64*1024): # 65536
    start, chunk =cut
    iter=0
    read_size=int(size)
    _break =False
    while not _break:
        if _break: break
        if f.tell()+size>start+chunk:
            read_size=int(start+chunk- f.tell() )
            _break=True
        b = f.read(read_size)
        iter +=1
        if not b: break
        yield b


def get_chunk_line_count(data):
    fn,  chunk_id, cut = data
    start, chunk =cut
    cnt =0
    last_bl=None

    with open(fn, "r") as f:
        if 0:
            f.seek(start)
            bl = f.read(chunk)
            cnt= bl.count('\n')
        else:
            f.seek(start)
            for i, bl  in enumerate(blocks(f,cut)):
                cnt +=  bl.count('\n')
                last_bl=bl

        if not last_bl.endswith('\n'):
            cnt -=1

        return cnt
....
pool = multiprocessing.Pool(processes=pool_size,
                            initializer=start_process,
                            )
pool_outputs = pool.map(get_chunk_line_count, inputs)
pool.close() # no more tasks
pool.join() 

Это улучшит производительность подсчета в 20 раз. Я завернул его в script и поместил его в Github.

Ответ 5

Быстрое однострочное решение:

sum((1 for i in open(file_path, 'rb')))

Он должен работать с файлами произвольного размера.