Ruby's File.open та необхідність f.close


92

У більшості мов програмування загальновідомо, що потік роботи з файлами є відкритим для використання. Тим не менше, я багато разів бачив у рубінових кодах неперевершені виклики File.open, і, крім того, я знайшов цю перлину знань у документах ruby:

Потоки вводу / виводу автоматично закриваються, коли на них вимагає збирач сміття.

darkredandyellow дружній irc береться до проблеми:
[17:12] так, а також, кількість дескрипторів файлів, як правило, обмежується ОС
[17:29] Я припускаю, ви можете легко закінчити доступні дескриптори файлів до того, як збирач сміття очистить вгору. у цьому випадку ви можете скористатись закрити їх самостійно. "заявляє смітник". означає, що ГК діє в якийсь момент у майбутньому. і це дорого. багато причин для явного закриття файлів.

  1. Чи потрібно нам явно закривати
  2. Якщо так, то чому GC автоматично закривається?
  3. Якщо ні, то чому варіант?

1
Ваші "загальновідомі відомості" застаріли з моменту винайдення деструкторів.
meagar

1
@meager: Коли були винайдені деструктори?
Andrew Grimm

Тільки примітка: Хоча дескриптори файлів обмежені, принаймні в Linux обмеження досить велике.
Linuxios

1
@Linuxios: у моєму ubuntu12.04 $ ulimit -n => 1024це високе лише тоді, коли ти робиш якусь просту роботу. Шкодна звичка одного разу спричинить великі проблеми!
HVNSсладіння

Відповіді:


133

Я багато разів бачив у рубінових кодах неперевершені File.openдзвінки

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

Досвідчені рубісти або явно закривають свої файли, або, що більш ідіоматично, використовують блокову форму File.open, яка автоматично закриває файл для вас. Його реалізація в основному виглядає приблизно так:

def File.open(*args, &block)
  return open_with_block(*args, &block) if block_given?
  open_without_block(*args)
end

def File.open_without_block(*args)
  # do whatever ...
end

def File.open_with_block(*args)
  yield f = open_without_block(*args)
ensure
  f.close
end

Сценарії - це особливий випадок. Зазвичай сценарії виконуються настільки коротко і використовують так мало дескрипторів файлів, що просто немає сенсу їх закривати, оскільки операційна система все одно закриє їх, коли сценарій вийде.

Чи потрібно нам явно закривати?

Так.

Якщо так, то чому GC автоматично закривається?

Оскільки після того, як він зібрав об’єкт, ви більше не можете закрити файл, і, отже, ви отримаєте дескриптори файлів.

Зверніть увагу, що не збирач сміття закриває файли. Збирач сміття просто виконує будь-які фіналізатори для об’єкта, перш ніж він його збирає. Так трапляється, що Fileклас визначає фіналізатор, який закриває файл.

Якщо ні, то чому варіант?

Оскільки марно витрачена пам’ять дешева, але марно витрачені дескриптори файлів - ні. Тому не має сенсу прив’язувати час життя дескриптора файлу до часу життя деякої частини пам'яті.

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


1
github.com/isaac/sunspot/blob/cell/sunspot/lib/sunspot/… +23 (хоча його ядро ​​# відкрито і використовується в основному для HTTP-сторони, але я все-таки дійшов до нього із параметром локального шляху до файлу). ..; я все ще намагаюся знайти час для виправлення та запиту-витягування), github.com/jnicklas/carrierwave Ctrl + f "File.open" (це наведено як приклад, але поганим чином ...), і кілька в інших місцях я не пам’ятаю. У мене проблема з проблемою через вимоги до стабільності в моїх проектах ..
clyfe

3
У цьому прикладі, чи слід підняти всередині рятувального блоку? Чи не буде це просто викинути помилку виконання, якщо буде викликано рейз і немає винятку?
Джеф Сторі

@JeffStorey: приємний улов! 17 місяців непомітно ...
Йорг Ш Міттаг

@ JörgWMittag і тепер ще 17 місяців не виправлено: PI, думаю, головне тут є ensure, rescueі raiseзовсім не потрібно.
KL-7, 7

Я думаю, ти не можеш ensureбез rescue. І ви не можете просто мовчки проковтнути виняток, вам потрібно передати його абоненту, закривши файл. У будь-якому разі, ще раз нагадай мені у травні '15 :-D
Йорг Ш Міттаг

71

Ви завжди повинні закривати дескриптори файлів після використання, що також змиє його. Часто люди використовують File.open або еквівалентний метод із блоками для обробки часу життя дескриптора файлу. Наприклад:

File.open('foo', 'w') do |f|
    f.write "bar"
end

У цьому прикладі файл закривається автоматично.


Гарна думка. Я відстежив помилку до сценарію, який не викликає File.close. Як результат, час від часу в деяких файлах буде відсутній останній рядок.
Ерван Легранд

Видатний. Я ніколи не знав цього фокусу. Так само, як Java-8 у цьому плані. Дякую.
sagneta

2

Відповідно до http://ruby-doc.org/core-2.1.4/File.html#method-c-open

Без пов'язаного блоку File.open є синонімом :: new. Якщо вказано додатковий блок коду, йому буде передано відкритий файл як аргумент, а об'єкт File буде автоматично закрито, коли блок завершується. Значення блоку буде повернуто з File.open.

Тому буде автоматично закрито, коли блок закінчується : D


1
  1. Так
  2. Якщо ви цього не зробите, або якщо буде якась інша помилка
  3. Див.2.

-3

Ми можемо використовувати File.read()функцію для читання файлу в ruby ​​....., наприклад,

file_variable = File.read("filename.txt")

у цьому прикладі file_variableможе мати повне значення цього файлу ....

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.