Necesito leer las últimas 25 líneas de un archivo (para mostrar las entradas de registro más recientes). ¿Hay alguna forma en Ruby para comenzar al final de un archivo y leerlo al revés?¿Lees las últimas n líneas de un archivo en Ruby?
Respuesta
No puedo responder por Ruby pero la mayoría de estos lenguajes siguen el lenguaje C de E/S de archivos. Eso significa que no hay forma de hacer lo que preguntas aparte de buscar. Esto generalmente toma uno de dos enfoques.
- Comenzando desde el inicio del archivo y escaneándolo todo, recordando las 25 líneas más recientes. Luego, cuando llegue al final del archivo, imprímalo.
- Un enfoque similar pero tratando de buscar primero una ubicación con mejor estimación. Eso significa buscar (por ejemplo) fin de archivo menos 4000 caracteres, y luego hacer exactamente lo que hizo en el primer acercamiento, con la condición de que, si no obtuvo 25 líneas, debe hacer una copia de seguridad y volver a intentarlo (por ejemplo, al final del archivo menos 5000 caracteres).
La segunda forma es la que yo prefiero ya que, si elige su primera compensación sabiamente, casi seguramente solo necesitará una oportunidad. Los archivos de registro todavía tienden a tener longitudes de línea máximas fijas (creo que los codificadores todavía tienen una propensión a los archivos de 80 columnas mucho después de que su utilidad se haya degradado). Tiendo a elegir el número de líneas deseado multiplicado por 132 como mi desplazamiento.
Y a partir de una mirada rápida de los documentos de Ruby en línea, parece que hace siguiendo el modismo C. Utilizaría "ios.seek(25*-132,IO::SEEK_END)"
si siguiera mi consejo, luego lea desde allí.
¿El archivo es lo suficientemente grande como para evitar leerlo todo? Si no es así, usted podría hacer
IO.readlines("file.log")[-25..-1]
Si es demasiado grande, es posible que necesite usar IO#seek
para leer de cerca del final del archivo, y seguir buscando hacia el principio hasta que haya visto 25 líneas.
Si no quiere pasar por la molestia de revertirlo, puede usar [-25 ..- 1] en su lugar. – sris
Beaty. No es necesario asumir nada sobre los comandos del sistema disponibles de esta manera. Gracias. :) –
@sris problema con [-25 ..- 1], si el archivo tiene menos de 25 líneas, entonces el resultado es nulo, recomendaría usar 'IO.readlines (" file.log "). Last (25) 'que devuelve matriz vacía en ese caso. –
Hay una biblioteca para Ruby llamada File::Tail. Esto puede obtener las últimas N líneas de un archivo como la utilidad de cola UNIX.
Asumo que hay algunos buscan la optimización en su lugar en la versión de UNIX de cola con puntos de referencia como estos (probado en un archivo de texto poco más de 11M):
[[email protected]]$du -sh 11M.txt
11M 11M.txt
[[email protected]]$time tail -n 25 11M.txt
/sbin/ypbind
/sbin/arptables
/sbin/arptables-save
/sbin/change_console
/sbin/mount.vmhgfs
/misc
/csait
/csait/course
/.autofsck
/~
/usb
/cdrom
/homebk
/staff
/staff/faculty
/staff/faculty/darlinr
/staff/csadm
/staff/csadm/service_monitor.sh
/staff/csadm/.bash_history
/staff/csadm/mysql5
/staff/csadm/mysql5/MySQL-server-community-5.0.45-0.rhel5.i386.rpm
/staff/csadm/glibc-common-2.3.4-2.39.i386.rpm
/staff/csadm/glibc-2.3.4-2.39.i386.rpm
/staff/csadm/csunixdb.tgz
/staff/csadm/glibc-headers-2.3.4-2.39.i386.rpm
real 0m0.012s
user 0m0.000s
sys 0m0.010s
Sólo puedo imaginar la librería Ruby utiliza una método similar.
Editar:
para la curiosidad de Pax:
[[email protected]]$time cat 11M.txt | tail -n 25
/sbin/ypbind
/sbin/arptables
/sbin/arptables-save
/sbin/change_console
/sbin/mount.vmhgfs
/misc
/csait
/csait/course
/.autofsck
/~
/usb
/cdrom
/homebk
/staff
/staff/faculty
/staff/faculty/darlinr
/staff/csadm
/staff/csadm/service_monitor.sh
/staff/csadm/.bash_history
/staff/csadm/mysql5
/staff/csadm/mysql5/MySQL-server-community-5.0.45-0.rhel5.i386.rpm
/staff/csadm/glibc-common-2.3.4-2.39.i386.rpm
/staff/csadm/glibc-2.3.4-2.39.i386.rpm
/staff/csadm/csunixdb.tgz
/staff/csadm/glibc-headers-2.3.4-2.39.i386.rpm
real 0m0.350s
user 0m0.000s
sys 0m0.130s
todavía en un segundo, pero si hay una gran cantidad de operaciones de archivo esto hace una gran diferencia.
¿Qué le proporciona "cat 11M.txt | tail -n 25"? Eso obligará a la cola a procesar toda la secuencia. – paxdiablo
O simplemente cat 11M.txt>/dev/null para ese asunto, eso le dará tiempo para procesar la transmisión, que bien puede ser del orden de 1/100 de segundo. – paxdiablo
Bugbear mío, @JohnT: "29 veces más lento" de 100 segundos es -2800 segundos. La frase correcta es "aproximadamente 1/29 de la velocidad". Pero entiendo tu punto: claramente tail está utilizando un método de búsqueda cuando tiene el archivo en lugar de una transmisión. Uno esperaría que Ruby sea tan inteligente también. – paxdiablo
Si en un sistema * nix con tail
, puede engañar de esta manera:
last_25_lines = `tail -n 25 whatever.txt`
Creo que una biblioteca sería más suficiente para la capacidad de plataforma cruzada, pero entiendes la idea. –
Probablemente cierto. No ejecuto código de Ruby en nada que no sea * sistemas basados en * nix y creo que tendrás dificultades para encontrar uno de esos sin 'cola' ... Además, a veces no puedes darte el lujo de instalar una biblioteca. Solo quería mostrar el 'one liner' :) – rfunduk
Hasta ahora no me he desplegado en un entorno que no tiene cola. Esta respuesta debe ser aceptada en mi opinión. –
¿Qué tal:
file = []
File.open("file.txt").each_line do |line|
file << line
end
file.reverse.each_with_index do |line, index|
puts line if index < 25
end
El rendimiento sería terrible sobre un archivo grande como se itera dos veces, el mejor enfoque sería el ya mencionado leer el archivo y almacenar las últimas 25 líneas en la memoria y mostrarlas. Pero esto fue solo un pensamiento alternativo.
acabo escribió un implemenation rápido con #seek
:
class File
def tail(n)
buffer = 1024
idx = (size - buffer).abs
chunks = []
lines = 0
begin
seek(idx)
chunk = read(buffer)
lines += chunk.count("\n")
chunks.unshift chunk
idx -= buffer
end while lines < n && pos != 0
chunks.join.lines.reverse_each.take(n).reverse.join
end
end
File.open('rpn-calculator.rb') do |f|
p f.tail(10)
end
En realidad, aunque tu código basado en búsquedas está cerca, no es del todo correcto porque no elimina la parte del fragmento anterior al primero \ n. Vea la nueva respuesta a continuación. :) –
: 11: en 'cola ': método indefinido' count' para nil: NilClass (NoMethodError) – Istvan
Versión mejorada del excelente solución basada en buscar de manveru. Este devuelve exactamente n líneas.
class File
def tail(n)
buffer = 1024
idx = [size - buffer, 0].min
chunks = []
lines = 0
begin
seek(idx)
chunk = read(buffer)
lines += chunk.count("\n")
chunks.unshift chunk
idx -= buffer
end while lines < (n + 1) && pos != 0
tail_of_file = chunks.join('')
ary = tail_of_file.split(/\n/)
lines_to_return = ary[ ary.size - n, ary.size - 1 ]
end
end
Ese código funciona en una Mac, pero falla en Linux con un mensaje de error "' cola ': variable local indefinida o método ' tamaño'". ¿Hay alguna idea de cómo arreglar eso? – earlyadopter
No hay verificación consolidada, lo que significa que puede leer y terminar buscando un idx negativo. Además, esto no está optimizado muy bien para un archivo con líneas muy largas (es decir, guardando los fragmentos en un búfer predestinado). Publicada una versión que se encarga de ambos. – Shai
Aquí hay una versión de la cola que no almacena ningún buffers en la memoria, mientras que ir, pero en su lugar utiliza "punteros". También hace la verificación de límite para que no termine buscando un desplazamiento negativo (si, por ejemplo, tiene más para leer pero menos que su tamaño de fragmento).
def tail(path, n)
file = File.open(path, "r")
buffer_s = 512
line_count = 0
file.seek(0, IO::SEEK_END)
offset = file.pos # we start at the end
while line_count <= n && offset > 0
to_read = if (offset - buffer_s) < 0
offset
else
buffer_s
end
file.seek(offset-to_read)
data = file.read(to_read)
data.reverse.each_char do |c|
if line_count > n
offset += 1
break
end
offset -= 1
if c == "\n"
line_count += 1
end
end
end
file.seek(offset)
data = file.read
end
casos de prueba en https://gist.github.com/shaiguitar/6d926587e98fc8a5e301
Esto no cierra el archivo. –
- 1. Java: lea las últimas n líneas de un archivo ENORME
- 2. Cómo leer las últimas "n" líneas del archivo de registro
- 3. Cola inversa/imprimir todo, excepto las últimas n líneas?
- 4. En vim, ¿cómo puedo eliminar todas las líneas en un archivo, excepto las últimas 100 líneas?
- 5. Leyendo las últimas líneas del archivo de texto comprimido
- 6. Bash edite el archivo y guarde las últimas 500 líneas
- 7. Cómo unir las primeras n líneas en un archivo
- 8. ¿Puedo grep solo las primeras n líneas de un archivo?
- 9. ¿Cómo puedo eliminar todas menos las últimas 10 líneas de un archivo?
- 10. Recuperar N últimas filas de un mysqldump
- 11. La forma más eficiente de buscar las últimas x líneas de un archivo en python
- 12. Subversion vertido últimas revisiones `N`
- 13. ¿Lees de un archivo creciente en C#?
- 14. ¿Cómo eliminar las dos primeras líneas y las últimas cuatro líneas de un archivo de texto con bash?
- 15. Extrayendo las últimas 10 líneas de un archivo que coincida con "foo"
- 16. ¿Lees un archivo de texto usando Node.js?
- 17. ¿Cómo lees esta condición ternaria en Ruby?
- 18. mysql seleccionar desde n últimas filas
- 19. ¿Lees una línea aleatoria de un archivo? C#
- 20. Iteración sobre las líneas de un archivo
- 21. C# ¿Cómo cuento las líneas en un archivo de texto
- 22. ¿Qué es lo opuesto a la cabeza? Quiero que todos, pero las primeras N líneas de un archivo
- 23. ¿Lees un archivo en App Engine con Python?
- 24. Cómo git-svn clona las últimas n revisiones desde un repositorio de Subversion?
- 25. ¿Obtiene las últimas N filas en la base de datos en orden?
- 26. Lee n líneas en [String]
- 27. ¿Omitiendo las primeras n líneas cuando usas regex con sed?
- 28. ¿es posible leer las últimas líneas (o 1000 caracteres) de una página web grande?
- 29. ¿Lees dos bytes en un número entero?
- 30. Cómo dividir las líneas del archivo de texto de Windows (separación de/r/n)
Todos mis terminales y emacs tampones son todavía 80 columnas de ancho; eso me permite colocar varios al lado del otro en mi monitor, lo cual es muy útil. –
Estoy bastante seguro de que la búsqueda de IO # va a ser la solución óptima, en cuanto a rendimiento. –