Ruby's File.open y la necesidad de f.close


92

Es de conocimiento común en la mayoría de los lenguajes de programación que el flujo para trabajar con archivos es abrir-usar-cerrar. Sin embargo, vi muchas veces en códigos ruby ​​llamadas File.open incomparables y, además, encontré esta joya de conocimiento en los documentos ruby:

Los flujos de E / S se cierran automáticamente cuando son reclamados por el recolector de basura.

irc amigable con darkredandyellow aborda el problema:
[17:12] sí, y también, el número de descriptores de archivo generalmente está limitado por el sistema operativo
[17:29] Supongo que puede quedarse sin descriptores de archivo disponibles antes de que el recolector de basura limpie arriba. en este caso, es posible que desee utilizar cerrarlos usted mismo. "Reclamado por el recolector de basura". significa que el GC actúa en algún momento en el futuro. y es caro. muchas razones para cerrar archivos explícitamente.

  1. ¿Necesitamos cerrar explícitamente
  2. En caso afirmativo, ¿por qué se cierra automáticamente el GC?
  3. Si no, ¿por qué la opción?

1
Su "conocimiento común" ha quedado obsoleto desde que se inventaron los destructores.
meagar

1
@meager: ¿Cuándo se inventaron los destructores?
Andrew Grimm

Solo una nota: si bien los descriptores de archivos son limitados, al menos en Linux, el límite es bastante alto.
Linuxios

1
@Linuxios: en mi ubuntu12.04 $ ulimit -n => 1024solo es alto cuando haces un trabajo simple. ¡El mal hábito causará un gran problema algún día!
HVNSweeting el

Respuestas:


133

Vi muchas veces en códigos ruby File.openllamadas inigualables

¿Puede dar un ejemplo? Solo veo eso en el código escrito por novatos que carecen del "conocimiento común en la mayoría de los lenguajes de programación de que el flujo para trabajar con archivos es abrir-usar-cerrar".

Los Rubyistas experimentados cierran explícitamente sus archivos o, más idiomáticamente, usan la forma de bloque de File.open, que cierra automáticamente el archivo para usted. Su implementación básicamente se parece a esto:

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

Los guiones son un caso especial. Los scripts generalmente se ejecutan tan cortos y usan tan pocos descriptores de archivo que simplemente no tiene sentido cerrarlos, ya que el sistema operativo los cerrará de todos modos cuando el script salga.

¿Necesitamos cerrar explícitamente?

Si.

En caso afirmativo, ¿por qué se cierra automáticamente el GC?

Porque una vez que ha recopilado el objeto, ya no hay forma de que cierre el archivo y, por lo tanto, filtrará descriptores de archivo.

Tenga en cuenta que no es el recolector de basura el que cierra los archivos. El recolector de basura simplemente ejecuta cualquier finalizador para un objeto antes de recolectarlo. Da la casualidad de que la Fileclase define un finalizador que cierra el archivo.

Si no, ¿por qué la opción?

Porque la memoria desperdiciada es barata, pero los descriptores de archivos desperdiciados no lo son. Por lo tanto, no tiene sentido vincular la vida útil de un descriptor de archivo con la vida útil de una parte de la memoria.

Simplemente no puede predecir cuándo se ejecutará el recolector de basura. Ni siquiera se puede predecir si se ejecutará en absoluto : si nunca se agota la memoria, el recolector de basura nunca se quedará, por tanto, el finalizador nunca se quedará, por lo tanto, el archivo nunca se cerró.


1
github.com/isaac/sunspot/blob/cell/sunspot/lib/sunspot/… +23 (aunque su Kernel # está abierto y se usa principalmente para el lado HTTP, pero lo alcancé con un parámetro de ruta de archivo local, sin embargo. ..; Todavía estoy tratando de encontrar tiempo para parchear & request-pull), github.com/jnicklas/carrierwave Ctrl + f "File.open" (se da como ejemplo, pero de mala manera ...), y varios otros lugares que no recuerdo. Tengo un problema con el problema debido a los requisitos de estabilidad en mis proyectos ..
clyfe

3
En este ejemplo, ¿debería elevarse dentro del bloque de rescate? ¿No arrojará esto un error de tiempo de ejecución si se llama a raise y no hay excepción?
Jeff Storey

@JeffStorey: ¡buena captura! 17 meses sin ser notado…
Jörg W Mittag

@ JörgWMittag y ahora 17 meses más sin arreglar: PI supongo que el punto principal aquí es ensure, rescuey raiseno son necesarios en absoluto.
KL-7 7 de

Creo que no puedes tener ensuresin rescue. Y no puede simplemente tragarse la excepción silenciosamente, debe propagarla a la persona que llama, después de haber cerrado el archivo. De todos modos, recuérdame de nuevo en mayo de 2015 :-D
Jörg W Mittag

71

Siempre debe cerrar los descriptores de archivos después de su uso, eso también los eliminará. A menudo, la gente usa File.open o un método equivalente con bloques para manejar la vida útil del descriptor de archivo. Por ejemplo:

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

En ese ejemplo, el archivo se cierra automáticamente.


Buen punto. Seguí un error en un script que no llama a File.close. Como resultado, la última línea faltará en algunos archivos de vez en cuando.
Erwan Legrand

Excepcional. Nunca supe este truco. Al igual que java-8 en ese sentido. Gracias.
sagneta

2

Según http://ruby-doc.org/core-2.1.4/File.html#method-c-open

Sin bloque asociado, File.open es sinónimo de :: new. Si se proporciona el bloque de código opcional, se pasará el archivo abierto como argumento y el objeto Archivo se cerrará automáticamente cuando finalice el bloque. El valor del bloque se devolverá desde File.open.

Por lo tanto, se cerrará automáticamente cuando finalice el bloque : D


1
  1. si
  2. En caso de que no lo haga, o si hay alguna otra falla
  3. Ver 2.

-3

Podemos usar la File.read()función para leer el archivo en ruby ​​... como,

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

en este ejemplo file_variablepuede tener el valor completo de ese archivo ...

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.