Ruby's File.open e la necessità di f.close


92

È risaputo nella maggior parte dei linguaggi di programmazione che il flusso per lavorare con i file sia open-use-close. Eppure ho visto molte volte nei codici ruby ​​chiamate File.open senza corrispondenza, e inoltre ho trovato questo gioiello di conoscenza nei documenti ruby:

I flussi di I / O vengono chiusi automaticamente quando vengono richiesti dal Garbage Collector.

darkredandyellow amichevole irc affronta la questione:
[17:12] sì, e inoltre, il numero di descrittori di file è solitamente limitato dal sistema operativo
[17:29] Presumo che tu possa facilmente esaurire i descrittori di file disponibili prima che il garbage collector pulisca su. in questo caso, potresti voler usare chiudi tu stesso. "rivendicato dal netturbino." significa che il GC agisce in futuro. ed è costoso. molte ragioni per chiudere i file in modo esplicito.

  1. Dobbiamo chiudere esplicitamente
  2. Se sì, perché il GC si chiude automaticamente?
  3. In caso contrario, perché l'opzione?

1
La tua "conoscenza comune" è stata superata da quando sono stati inventati i distruttori.
meagar

1
@meager: quando sono stati inventati i distruttori?
Andrew Grimm

Solo una nota: sebbene i descrittori di file siano limitati, almeno su Linux, il limite è piuttosto alto.
Linuxios

1
@Linuxios: sul mio ubuntu12.04 $ ulimit -n => 1024è alto solo quando fai solo un semplice lavoro. Una cattiva abitudine causerà un grosso problema un giorno!
HVNSweeting

Risposte:


133

Ho visto molte volte File.openchiamate senza corrispondenza nei codici Ruby

Puoi fare un esempio? Vedo solo che nel codice scritto da principianti a cui manca la "conoscenza comune nella maggior parte dei linguaggi di programmazione che il flusso per lavorare con i file è aperto-usa-chiudi".

Gli esperti Rubyists chiudono esplicitamente i loro file o, più idiomaticamente, usano la forma di blocco di File.open, che chiude automaticamente il file per te. La sua implementazione è fondamentalmente simile a questa:

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

Gli script sono un caso speciale. Gli script generalmente sono così brevi e usano così pochi descrittori di file che semplicemente non ha senso chiuderli, poiché il sistema operativo li chiuderà comunque quando lo script esce.

Dobbiamo chiudere esplicitamente?

Sì.

Se sì, perché il GC si chiude automaticamente?

Perché dopo che ha raccolto l'oggetto, non è più possibile chiudere il file e quindi si perdono i descrittori dei file.

Nota che non è il garbage collector che chiude i file. Il garbage collector esegue semplicemente qualsiasi finalizzatore per un oggetto prima che lo raccolga. Accade così che la Fileclasse definisca un finalizzatore che chiude il file.

In caso contrario, perché l'opzione?

Perché la memoria sprecata costa poco, ma i descrittori di file sprecati no. Pertanto, non ha senso legare la durata di un descrittore di file alla durata di qualche pezzo di memoria.

Semplicemente non è possibile prevedere quando verrà eseguito il Garbage Collector. Non si può nemmeno prevedere se verrà eseguito a tutti : se mai a corto di memoria, il garbage collector non potrà mai funzionare, quindi il finalizzatore verrà mai eseguito, quindi il file non verrà mai chiusa.


1
github.com/isaac/sunspot/blob/cell/sunspot/lib/sunspot/… +23 (sebbene il suo kernel # sia aperto e utilizzato principalmente per il lato HTTP, ma l'ho raggiunto con un parametro del percorso del file locale, comunque. ..; Sto ancora cercando di trovare il tempo per applicare patch e richiesta-pull), github.com/jnicklas/carrierwave Ctrl + f "File.open" (è dato come esempio, ma in modo negativo ...) e molti altri posti che non ricordo. Ho un problema con il problema a causa dei requisiti di stabilità nei miei progetti ..
Clyfe

3
In questo esempio, il rilancio dovrebbe essere all'interno del blocco di salvataggio? Questo non genererà semplicemente un errore di runtime se viene chiamato raise e non ci sono eccezioni?
Jeff Storey

@ JeffStorey: bella cattura! 17 mesi inosservati ...
Jörg W Mittag

@ JörgWMittag e ora altri 17 mesi non risolti: PI immagino che il punto principale qui sia ensure, rescuee raisenon sono affatto necessari.
KL-7 7

Penso che non puoi averne ensuresenza rescue. E non puoi semplicemente ingoiare silenziosamente l'eccezione, devi propagarla al chiamante, dopo aver chiuso il file. Comunque, ricordamelo di nuovo a maggio '15 :-D
Jörg W Mittag

71

Dovresti sempre chiudere i descrittori di file dopo l'uso, anche questo lo scaricherà. Spesso le persone usano File.open o un metodo equivalente con blocchi per gestire la durata del descrittore di file. Per esempio:

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

In questo esempio il file viene chiuso automaticamente.


Buon punto. Ho tracciato un bug su uno script che non chiama File.close. Di conseguenza, l'ultima riga mancherà di tanto in tanto in alcuni file.
Erwan Legrand

Eccezionale. Non ho mai conosciuto questo trucco. Proprio come java-8 a questo proposito. Grazie.
sagneta

2

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

Senza blocchi associati, File.open è sinonimo di :: new. Se viene fornito il blocco di codice opzionale, verrà passato il file aperto come argomento e l'oggetto File verrà automaticamente chiuso quando il blocco termina. Il valore del blocco verrà restituito da File.open.

Pertanto, verrà automaticamente chiuso quando il blocco termina : D


1
  1. Nel caso in cui non lo fai, o se c'è qualche altro fallimento
  2. Vedi 2.

-3

Possiamo usare la File.read()funzione per leggere il file in ruby ​​..... come,

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

in questo esempio file_variablepuò avere il valore completo di quel file ....

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.