Estoy escribiendo un script de importación que procesa un archivo que tiene potencialmente cientos de miles de líneas (archivo de registro). El uso de un enfoque muy simple (a continuación) llevó suficiente tiempo y memoria como para pensar que eliminaría mi MBP en cualquier momento, así que maté el proceso.Cómo analizar eficazmente archivos de texto grandes en Ruby
#...
File.open(file, 'r') do |f|
f.each_line do |line|
# do stuff here to line
end
end
Este archivo en particular, tiene 642,868 líneas:
$ wc -l nginx.log /code/src/myimport
642868 ../nginx.log
¿Alguien sabe de una manera más eficiente (memoria/CPU) para procesar cada línea de este archivo?
ACTUALIZACIÓN
El código dentro de la f.each_line
desde arriba es simplemente que coincidan con una expresión regular contra la línea. Si la coincidencia falla, agrego la línea a una matriz @skipped
. Si pasa, formateo las coincidencias en un hash (codificado por los "campos" de la coincidencia) y lo adjunto a una matriz @results
.
# 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
Estoy completamente abierto a que esto sea un proceso ineficiente. Podría hacer que el código dentro de convert_to_hash
use un lambda precalculado en lugar de calcular el cálculo cada vez. Supongo que simplemente asumí que era la iteración de línea en sí el problema, no el código por línea.
La forma más eficiente de la memoria es cómo lo está haciendo con 'each_line'. Podrías leer el archivo en bloques que es más rápido, luego usar 'String # lines' para tomar líneas individuales junto con volver a unir cualquier línea parcialmente cargada que cruzara los límites del bloque. Se convierte en un lavado tener que dividir las líneas y reunirse con las rotas. –