Чтение последних n строк файла в Ruby?

Если ваша проблема связана с утечками памяти, вам следует использовать free (fb) для динамически размещаемых указателей.

30
задан Mike Woodhouse 16 April 2009 в 08:45
поделиться

5 ответов

Если в системе * nix с хвостом , вы можете обмануть так:

last_25_lines = `tail -n 25 whatever.txt`
36
ответ дан 27 November 2019 в 23:06
поделиться

Достаточно ли велик файл, чтобы вам не пришлось читать его целиком? Если нет, вы можете просто сделать

IO.readlines("file.log")[-25..-1]

. Если оно слишком большое, вам может потребоваться использовать IO # seek для чтения из конца файла и продолжать поиск в начале, пока вы не видел 25 строк.

25
ответ дан 27 November 2019 в 23:06
поделиться

There is a library for Ruby called File::Tail. This can get you the last N lines of a file just like the UNIX tail utility.

I assume there is some seek optimization in place in the UNIX version of tail with benchmarks like these (tested on a text file just over 11M):

[john@awesome]$du -sh 11M.txt
11M     11M.txt
[john@awesome]$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

I can only imagine the Ruby library uses a similar method.

Edit:

for Pax's curiosity:

[john@awesome]$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

still under a second, but if there is a lot of file operations this makes a big difference.

14
ответ дан 27 November 2019 в 23:06
поделиться

Я не могу поручиться за Ruby, но большинство из этих языков придерживаются языка ввода-вывода файлов. Это означает, что нет способа сделать то, что вы просите, кроме поиска. Обычно для этого используется один из двух подходов.

  • Начиная с начала файла и сканируя все, запоминая последние 25 строк. Затем, когда вы нажмете на конец файла, распечатайте их.
  • Аналогичный подход, но сначала вы пытаетесь найти лучшее место. Это означает поиск (например) конца файла минус 4000 символов, а затем выполнение именно того, что вы делали в первом подходе, при условии, что, если вы не получили 25 строк, вы должны выполнить резервное копирование и повторить попытку (например, до конца файла минус 5000 символов).

Второй способ - тот, который я предпочитаю, поскольку, если вы правильно выберете первое смещение, вы ' Почти наверняка понадобится только один выстрел. Файлы журналов, как правило, по-прежнему имеют фиксированную максимальную длину строки (я думаю, что кодеры все еще имеют склонность к файлам с 80 столбцами еще после того, как их полезность снизилась). Я склонен выбирать количество желаемых строк, умноженное на 132, в качестве моего смещения.

И из беглого взгляда на документы по Ruby онлайн, похоже, что следует , следуя идиоме Си. Вы бы использовали «ios.seek (25 * -132, IO :: SEEK_END)» , если бы следовали моему совету, а затем читайте дальше оттуда.

похоже, он следует , следуя языку C. Вы бы использовали «ios.seek (25 * -132, IO :: SEEK_END)» , если бы следовали моему совету, а затем читайте дальше оттуда.

похоже, он следует , следуя языку C. Вы бы использовали «ios.seek (25 * -132, IO :: SEEK_END)» , если бы следовали моему совету, а затем читайте дальше оттуда.

4
ответ дан 27 November 2019 в 23:06
поделиться

Как насчет:

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

Производительность была бы ужасной для большого файла, поскольку он повторяется дважды, лучшим подходом было бы уже упомянутое чтение файла и сохранить в памяти последние 25 строк и отобразить их. Но это была альтернативная мысль.

0
ответ дан 27 November 2019 в 23:06
поделиться
Другие вопросы по тегам:

Похожие вопросы: