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

92

В большинстве языков программирования общеизвестно, что процесс работы с файлами открыт-используется-закрывается. Тем не менее, я много раз видел в кодах Ruby непревзойденные вызовы File.open, и, более того, я нашел эту жемчужину знаний в документации ruby:

Потоки ввода-вывода автоматически закрываются, когда они запрашиваются сборщиком мусора.

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

  1. Нужно ли явно закрывать
  2. Если да, то почему ГХ автоматически закрывается?
  3. Если нет, то почему вариант?
клиф
источник
1
Ваши «общие знания» устарели с момента изобретения деструкторов.
meagar
1
@meager: Когда были изобретены деструкторы?
Эндрю Гримм
Просто примечание: хотя количество дескрипторов файлов ограничено, по крайней мере, в Linux, ограничение довольно велико.
Linuxios
1
@Linuxios: на моем ubuntu12.04 $ ulimit -n => 1024он высокий только тогда, когда вы выполняете простую работу. Плохая привычка однажды вызовет большие проблемы!
HVNSweeting

Ответы:

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

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

Нужно ли закрывать явно?

Да.

Если да, то почему ГХ автоматически закрывается?

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

Обратите внимание, что файлы закрывает не сборщик мусора. Сборщик мусора просто выполняет любые финализаторы для объекта, прежде чем он его соберет. Так уж получилось, что Fileкласс определяет финализатор, который закрывает файл.

Если нет, то почему вариант?

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

Вы просто не можете предсказать, когда запустится сборщик мусора. Вы даже не можете предсказать , запустится ли он вообще : если у вас никогда не закончится память, сборщик мусора никогда не запустится, поэтому финализатор никогда не запустится, поэтому файл никогда не будет закрыт.

Йорг В. Миттаг
источник
1
github.com/isaac/sunspot/blob/cell/sunspot/lib/sunspot/… +23 (хотя его номер ядра открыт и используется в основном для HTTP-стороны, но я все же достиг его с параметром локального пути к файлу. ..; Я все еще пытаюсь найти время, чтобы исправить & request-pull), github.com/jnicklas/carrierwave Ctrl + f "File.open" (это дано в качестве примера, но в плохом смысле ...) и несколько другие места я не помню. У меня есть претензии к этой проблеме из-за требований к стабильности в моих проектах ..
clyfe
3
В этом примере, должно ли повышение быть внутри блока спасения? Разве это не вызовет ошибку времени выполнения, если вызывается Raise и нет исключения?
Джефф Стори,
@JeffStorey: хороший улов! 17 месяцев незамеченными…
Jörg W Mittag
@ JörgWMittag, а теперь еще 17 месяцев не исправлено: я думаю, главное здесь есть ensure, rescueа в raiseних нет необходимости.
KL-7 07
Я думаю, без тебя не ensureобойтись rescue. И вы не можете просто молча проглотить исключение, вам нужно распространить его на вызывающую программу после закрытия файла. В любом случае, напомните мне еще раз в мае '15 :-D
Jörg W Mittag
72

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

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

В этом примере файл закрывается автоматически.

Tonttu
источник
Хорошая точка зрения. Я обнаружил ошибку в скрипте, который не вызывает 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

skozz
источник
1
  1. да
  2. В случае, если вы этого не сделаете, или если возникнет другой сбой
  3. См. 2.
Сатья
источник
-3

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

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

в этом примере file_variableможет иметь полное значение этого файла ....

Кумар К.С.
источник