У меня есть проблема с обработкой largeish файла в Python. Все, что я делаю,
f = gzip.open(pathToLog, 'r')
for line in f:
counter = counter + 1
if (counter % 1000000 == 0):
print counter
f.close
Это берет вокруг 10m25 s только, чтобы открыть файл, считать строки и увеличить этот счетчик.
В жемчуге, имея дело с тем же файлом и делая вполне немного больше (некоторый материал регулярного выражения), целый процесс берет вокруг 1m17 s.
Код Perl:
open(LOG, "/bin/zcat $logfile |") or die "Cannot read $logfile: $!\n";
while (<LOG>) {
if (m/.*\[svc-\w+\].*login result: Successful\.$/) {
$_ =~ s/some regex here/$1,$2,$3,$4/;
push @an_array, $_
}
}
close LOG;
Кто-либо может советовать тому, что я могу сделать для создания решения Python выполненным на подобной скорости к решению для Perl?
РЕДАКТИРОВАНИЕ, которое я попытался просто распаковать файл и иметь дело с ним с помощью открытого вместо gzip.open, но который только изменяет общее время приблизительно на 4m14.972 s, который является все еще слишком медленным.
Я также удалил и операторы печати по модулю и заменил их передачей, таким образом, все, что делается теперь, перемещается от файла до файла.
В Python (как минимум <= 2.6.x) синтаксический анализ формата gzip реализован в Python (поверх zlib). Более того, похоже, что он делает некоторые странные вещи, а именно, распаковывает до конца файла в память, а затем отбрасывает все, что превышает запрошенный размер чтения (затем делает это снова для следующего чтения). ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ : Я только что просмотрел gzip.read ()
в течение 3 минут, поэтому могу ошибаться. Независимо от моего понимания gzip.read () верен или нет, модуль gzip не оптимизирован для больших объемов данных. Попробуйте сделать то же самое, что и в Perl, т.е. запустить внешний процесс (например, см. Подпроцесс модуля
).
РЕДАКТИРОВАТЬ На самом деле, я пропустил замечание ОП о том, что ввод-вывод простого файла столь же медленен, как и сжатый (спасибо ire_and_curses за указание на это). Это показалось мне маловероятным, поэтому я провел некоторые измерения ...
from timeit import Timer
def w(n):
L = "*"*80+"\n"
with open("ttt", "w") as f:
for i in xrange(n) :
f.write(L)
def r():
with open("ttt", "r") as f:
for n,line in enumerate(f) :
if n % 1000000 == 0 :
print n
def g():
f = gzip.open("ttt.gz", "r")
for n,line in enumerate(f) :
if n % 1000000 == 0 :
print n
Теперь, запустив ...
>>> Timer("w(10000000)", "from __main__ import w").timeit(1)
14.153118133544922
>>> Timer("r()", "from __main__ import r").timeit(1)
1.6482770442962646
# here i switched to a terminal and made ttt.gz from ttt
>>> Timer("g()", "from __main__ import g").timeit(1)
... и после перерыва на чай, обнаружив, что он все еще работает, я убил его, извините . Затем я попробовал 100 000 строк вместо 10 000 000:
>>> Timer("w(100000)", "from __main__ import w").timeit(1)
0.05810999870300293
>>> Timer("r()", "from __main__ import r").timeit(1)
0.09662318229675293
# here i switched to a terminal and made ttt.gz from ttt
>>> Timer("g()", "from __main__ import g").timeit(1)
11.939290046691895
Время модуля gzip равно O (file_size ** 2), поэтому при количестве строк порядка миллионов время чтения gzip не может быть таким же, как обычное время чтения (как мы видим, подтверждено экспериментом). Anonymouslemming, проверьте еще раз.
Если вы погуглите, "почему python gzip медленный", вы найдете множество обсуждений этого вопроса, включая исправления для улучшений в Python 2.7 и 3.2. А пока используйте zcat, как вы делали это в Perl, который очень быстро работает. Ваша (первая) функция занимает у меня около 4,19 секунды со сжатым файлом размером 5 МБ, а вторая функция занимает 0,78 секунды. Однако я не знаю, что происходит с вашими несжатыми файлами. Если я распакую файлы журналов (журналы apache) и запустил на них две функции с простым Python open (file) и Popen ('cat'), Python будет быстрее (0,17 с), чем cat (0,48 с).
#!/usr/bin/python import gzip from subprocess import PIPE, Popen import sys import timeit #pathToLog = 'big.log.gz' # 50M compressed (*10 uncompressed) pathToLog = 'small.log.gz' # 5M "" def test_ori(): counter = 0 f = gzip.open(pathToLog, 'r') for line in f: counter = counter + 1 if (counter % 100000 == 0): # 1000000 print counter, line f.close def test_new(): counter = 0 content = Popen(["zcat", pathToLog], stdout=PIPE).communicate()[0].split('\n') for line in content: counter = counter + 1 if (counter % 100000 == 0): # 1000000 print counter, line if '__main__' == __name__: to = timeit.Timer('test_ori()', 'from __main__ import test_ori') print "Original function time", to.timeit(1) tn = timeit.Timer('test_new()', 'from __main__ import test_new') print "New function time", tn.timeit(1)
Ваш компьютер занял 10 минут? Это должно быть ваше оборудование. Я написал эту функцию для записи 5 миллионов строк:
def write():
fout = open('log.txt', 'w')
for i in range(5000000):
fout.write(str(i/3.0) + "\n")
fout.close
Затем я прочитал ее с помощью программы, очень похожей на вашу:
def read():
fin = open('log.txt', 'r')
counter = 0
for line in fin:
counter += 1
if counter % 1000000 == 0:
print counter
fin.close
Моему компьютеру потребовалось около 3 секунд, чтобы прочитать все 5 миллионов строк.
Я потратил некоторое время на это. Надеюсь, этот код поможет. Он использует zlib и не использует внешние вызовы.
Метод gunzipchunks считывает сжатый файл gzip частями, которые можно повторять (генератор).
Метод gunziplines считывает эти несжатые фрагменты и предоставляет вам одну строку за раз, которую также можно повторять (другой генератор).
Наконец, метод gunziplinescounter дает вам то, что вы ищете.
Ура!
import zlib
file_name = 'big.txt.gz'
#file_name = 'mini.txt.gz'
#for i in gunzipchunks(file_name): print i
def gunzipchunks(file_name,chunk_size=4096):
inflator = zlib.decompressobj(16+zlib.MAX_WBITS)
f = open(file_name,'rb')
while True:
packet = f.read(chunk_size)
if not packet: break
to_do = inflator.unconsumed_tail + packet
while to_do:
decompressed = inflator.decompress(to_do, chunk_size)
if not decompressed:
to_do = None
break
yield decompressed
to_do = inflator.unconsumed_tail
leftovers = inflator.flush()
if leftovers: yield leftovers
f.close()
#for i in gunziplines(file_name): print i
def gunziplines(file_name,leftovers="",line_ending='\n'):
for chunk in gunzipchunks(file_name):
chunk = "".join([leftovers,chunk])
while line_ending in chunk:
line, leftovers = chunk.split(line_ending,1)
yield line
chunk = leftovers
if leftovers: yield leftovers
def gunziplinescounter(file_name):
for counter,line in enumerate(gunziplines(file_name)):
if (counter % 1000000 != 0): continue
print "%12s: %10d" % ("checkpoint", counter)
print "%12s: %10d" % ("final result", counter)
print "DEBUG: last line: [%s]" % (line)
gunziplinescounter(file_name)
Это должно работать намного быстрее, чем использование встроенного модуля gzip для очень больших файлов.