2011-01-30 19 views
6

Yüz binlerce satır (günlük dosyası) içeren bir dosyayı işleyen bir alma komut dosyası yazıyorum. Çok basit bir yaklaşım kullanarak (aşağıda) MBP'yi her an alabileceğimi hissettiğim zaman ve bellek aldım, bu yüzden süreci öldürdüm. Ruby'de büyük metin dosyalarını verimli bir şekilde nasıl ayrıştırabilirim?

#... 
File.open(file, 'r') do |f| 
    f.each_line do |line| 
    # do stuff here to line 
    end 
end 

özellikle Bu dosya 642868 hatları vardır:

$ wc -l nginx.log                                  /code/src/myimport 
    642868 ../nginx.log 

herkes bu dosyadaki her satır işlemek için daha verimli (bellek/cpu) yol biliyor mu?

GÜNCELLEME

yukarıdan f.each_line iç kod basitçe çizgisine karşı bir normal ifade eşleştirme. Eşleşme başarısız olursa, satırı @skipped dizisine ekliyorum. Geçerse, eşleşmeleri bir eşiğe (eşleşmenin "alanları" tarafından girilen) biçimlendirir ve onu bir @results dizisine eklerim.

# regex built in `def initialize` (not on each line iteration) 
@regex = /(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}) - (.{0})- \[([^\]]+?)\] "(GET|POST|PUT|DELETE) ([^\s]+?) (HTTP\/1\.1)" (\d+) (\d+) "-" "(.*)"/ 

#... loop lines 
match = line.match(@regex) 
if match.nil? 
    @skipped << line 
else 
    @results << convert_to_hash(match) 
end 

Bunun için verimsiz bir süreç olana tamamen açıkım. convert_to_hash'un içindeki kodu, her seferinde hesaplamayı bulmak yerine önceden oluşturulmuş bir lambda kullanabiliyordum. Sanırım sadece satır yinelemenin kendisinin sorun değil, satır kodunun kendisi olduğunu varsaydım.

+0

Bir göz atın en bellek verimli yolu, her_line ile bunu yapıyoruz. Dosyayı daha hızlı olan bloklarda okuyabiliyordunuz, daha sonra blok sınırlarını aşan kısmi yüklü satırları yeniden birleştirmek için tek tek satırları kapmak için 'String # lines' kullanın. Çizgileri ayırmak ve kırık olanları yeniden birleştirmek için bir yıkama olur. –

cevap

5

Sadece 600.000 satırlık bir dosya üzerinde bir test yaptım ve bu dosya üzerinde yarım saniyeden daha az bir süre boyunca yinelendi. Sanırım yavaşlık dosya döngüsünde değil, satır ayrıştırmada. Ayrıştırma kodunuzu da yapıştırabilir misiniz? input.rb olarak

:

+0

Herhangi bir önemi olan tek kod parçası, çizgiyi yarı karmaşık bir regex ile eşleştirdiğimdir. Regex herhangi bir geri/ileriye doğru bakmaz, çoğunlukla sadece char-by-char eşleşir. İlgili kodu içeren bir güncelleme yayınlayacağım. – localshred

+0

Oh ve normal ifadeler, her yinelemede değil (yalnızca açık olarak) bir kez hesaplanır. – localshred

+0

Bellek büyümesine neden olan benim aptallığım olduğu anlaşılıyor. Daha sonra db eklemek için kullandığım dizilerde (veya atlamaların boyutunu basarken) eşleşen sonuçları (ve ayrıca atlanan satırları) saklıyordum. Biliyorum, ben aptalım.:) Şimdi sadece atlanmış hatlarda bir "koçluk" yapıyorum ve maç geçerli olduğunda db ekini yapıyorum. Asıl memesi asla 30mb'nin üzerine çıkmaz. Muhtemelen aptalca bir şey yaptığımı gösterdiğiniz için teşekkürler. :) (Oh ve ben orijinal cevabınız gibi IO.foreach'a geçtim). – localshred

1

Eğer böyle optimize etmek mümkün olabilir (veya benzeri) bash kullanıyorsanız

bash sonra
while x = gets 
     # Parse 
end 

:

cat nginx.log | ruby -n input.rb 

-n flag, rubeyi assume 'while gets(); ... end' loop around your script numaralı telefona bildirir, bu da optimize etmek için özel bir şey yapmasına neden olabilir.

Ayrıca, daha hızlı olacağından, önceden yazılan bir soruna sorunla bakmak isteyebilirsiniz.

+0

Bu noktada istediğimden biraz daha haince görünüyor, ama aklımda tutacağım. – localshred

4

Bu blogpost, büyük günlük dosyalarını ayrıştırmada çeşitli yaklaşımlar içerir. Belki de bu bir ilham kaynağıdır. Ayrıca, file-tail gem

İlgili konular