Які всі поширені способи читання файлу в Ruby?


280

Які всі поширені способи читання файлу в Ruby?

Наприклад, ось один метод:

fileObj = File.new($fileName, "r")
while (line = fileObj.gets)
  puts(line)
end
fileObj.close

Я знаю, що Рубі надзвичайно гнучка. Які переваги / недоліки кожного підходу?


6
Я не вважаю, що поточна відповідь на перемогу є правильною.
inger

Відповіді:


259
File.open("my/file/path", "r") do |f|
  f.each_line do |line|
    puts line
  end
end
# File is closed automatically at end of block

Також можна явно закрити файл після, як зазначено вище (передати блок до open закрити його для вас):

f = File.open("my/file/path", "r")
f.each_line do |line|
  puts line
end
f.close

14
Це навряд чи ідіоматичний Рубі. Використовуйте foreachзамість цього openі не обходиться each_lineблоком.
Олов'яний чоловік

7
f.each { |line| ... }і, f.each_line { |line| ... }схоже, мають таку саму поведінку (принаймні, у Ruby 2.0.0).
chbrown

327

Найпростіший спосіб, якщо файл не надто довгий:

puts File.read(file_name)

Дійсно, IO.readабо File.readавтоматично закриваємо файл, тому немає необхідності використовувати File.openз блоком.


16
IO.readабо File.readтакож автоматично закривати файл, хоча у вашому формулюванні це звучить так, як ні.
Фрогз

15
він уже сказав "якщо файл не надто довгий". Цілком відповідає моєму випадку.
jayP

227

Будьте обережні з "рогалими" файлами. Ось тоді ви читаєте весь файл в пам'ять одразу.

Проблема в тому, що вона не масштабується добре. Ви можете розробити код з файлом досить великого розміру, а потім поставити його у виробництво і раптом виявити, що ви намагаєтесь прочитати файли розміром у гігабайти, і ваш хост заморожується, коли він намагається прочитати та виділити пам'ять.

Лінійний ввід / вивід дуже швидкий і майже завжди такий же ефективний, як і рогатка. Насправді це дивно швидко.

Мені подобається використовувати:

IO.foreach("testfile") {|x| print "GOT ", x }

або

File.foreach('testfile') {|x| print "GOT", x }

Файл успадковується від IO і foreachзнаходиться в IO, тому ви можете використовувати будь-який.

У мене є деякі орієнтири, які показують вплив спроб читання великих файлів через readвхідний / вивідний рядковий рядок у розділі " Чому" прошивання "файлу не є хорошою практикою? ".


6
Це саме те, що я шукав. У мене є файл з п’ятьма мільйонами рядків, і я дуже не хотів, щоб це завантажувалося в пам'ять.
Скотті C.

68

Ви можете прочитати файл одразу:

content = File.readlines 'file.txt'
content.each_with_index{|line, i| puts "#{i+1}: #{line}"}

Коли файл великий або може бути великий, зазвичай краще обробляти його по черзі:

File.foreach( 'file.txt' ) do |line|
  puts line
end

Іноді ви хочете отримати доступ до ручки файлу, хоча або контролюєте прочитані самі:

File.open( 'file.txt' ) do |f|
  loop do
    break if not line = f.gets
    puts "#{f.lineno}: #{line}"
  end
end

У разі двійкових файлів ви можете вказати нульовий роздільник і розмір блоку, наприклад:

File.open('file.bin', 'rb') do |f|
  loop do
    break if not buf = f.gets(nil, 80)
    puts buf.unpack('H*')
  end
end

Нарешті, ви можете зробити це без блоку, наприклад, обробляючи кілька файлів одночасно. У цьому випадку файл повинен бути явно закритий (покращений відповідно до коментаря @antinome):

begin
  f = File.open 'file.txt'
  while line = f.gets
    puts line
  end
ensure
  f.close
end

Посилання: API файлів та API IO .


2
Немає for_eachфайлу або IO. Використовуйте foreachзамість цього.
The Tin Man

1
Зазвичай я використовую редактор Sublime Text з плагіном RubyMarkers, коли документує код, який використовується тут у відповідях. Це робить дійсно легко показати проміжні результати, подібні до використання IRB. Також плагін Seeing Is Believing для Sublime Text 2 є дуже потужним.
Олов'яний чоловік

1
Чудова відповідь. Для останнього прикладу я можу запропонувати використовувати whileзамість цього loopі використовувати ensureдля того, щоб файл закривався, навіть якщо виняток виноситься. Як це (замінити крапку з комою з новим рядком): begin; f = File.open('testfile'); while line = f.gets; puts line; end; ensure; f.close; end.
antinome

1
так, це набагато краще @antinome, покращив відповідь. Дякую!
Віктор Клос

26

Одним з простих методів є використання readlines:

my_array = IO.readlines('filename.txt')

Кожен рядок у вхідному файлі буде записом у масиві. Метод обробляє відкриття та закриття файлу для вас.


5
Як readі для будь-якого варіанту, і це дозволить перетягнути весь файл у пам'ять, що може спричинити великі проблеми, якщо файл більше, ніж наявна пам'ять. Крім того, оскільки це масив, Ruby повинен створити масив, додатково уповільнивши процес.
Олов'яний чоловік


9

Я зазвичай роблю це:

open(path_in_string, &:read)

Це дасть вам весь текст у вигляді рядкового об’єкта. Він працює лише під Ruby 1.9.


Це приємно і коротко! Чи закриває файл також?
mrgreenfur

5
Це закриває його, але це не масштабується, тому будьте обережні.
Олов'яний чоловік

3

повернути останні n рядків з your_file.log або .txt

path = File.join(Rails.root, 'your_folder','your_file.log')

last_100_lines = `tail -n 100 #{path}`

1

Ще більш ефективним способом є потокове передавання, попросивши ядро ​​операційної системи відкрити файл, а потім читати байти з нього побіжно. Під час читання файлу на рядок у Ruby дані беруться за один раз із 512 байтів файлу та розбиваються на "рядки" після цього.

Буферизуючи вміст файлу, кількість викликів вводу / виводу зменшується під час ділення файлу на логічні фрагменти.

Приклад:

Додайте цей клас у додаток як об’єкт обслуговування:

class MyIO
  def initialize(filename)
    fd = IO.sysopen(filename)
    @io = IO.new(fd)
    @buffer = ""
  end

  def each(&block)
    @buffer << @io.sysread(512) until @buffer.include?($/)

    line, @buffer = @buffer.split($/, 2)

    block.call(line)
    each(&block)
  rescue EOFError
    @io.close
 end
end

Викличте його та передайте :eachметоду блок:

filename = './somewhere/large-file-4gb.txt'
MyIO.new(filename).each{|x| puts x }

Про це читайте тут у цій детальній публікації:

Ruby Magic Slurping & Streaming Files by AppSignal


Будьте уважні: цей код буде ігнорувати останній рядок, якщо він не закінчується зворотним посиланням (принаймні в Linux).
Йорген

Я думаю, що вставляючи "block.call (@buffer)" перед "@ io.close", підбере відсутній неповний рядок. Однак я грав з Рубі лише один день, тож я міг помилятися. Це спрацювало в моїй заявці :)
Йорген

Після прочитання публікації AppSignal здається, що тут сталося невелике непорозуміння. Код, який ви скопіювали з цієї посади, яка робить буферизований IO, є прикладом реалізації того, що насправді робить Ruby з File.foreach або IO.foreach (які є тим же методом). Їх слід використовувати, і вам не потрібно їх повторне доповнення таким чином.
Пітер Х. Болінг

@ PeterH.Boling Я також також використовую менталітет щодо використання та не повторного втілення. Але рубін дозволяє нам відкривати речі і без сорому заглядати в їх нутро, це один з перків. Немає справжнього "повинен" або "не повинен", особливо в рубінах / рейках. Поки ви знаєте, чим займаєтесь, і ви пишете тести на це.
Халил Гарбауї

0
content = `cat file`

Я думаю, що цей метод є найбільш "незвичайним". Можливо, це щось хитро, але це працює, якщо catвін встановлений.


1
Зручний трюк, але виклик в оболонку має безліч підводних каменів, включаючи 1) команди можуть відрізнятися в різних ОС, 2) вам може знадобитися уникнути пробілів у назві файлу. Вам набагато краще використовувати вбудовані функції Ruby, наприкладcontent = File.read(filename)
Jeff Ward
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.