Ruby File.open et le besoin de f.close


92

Il est de notoriété publique dans la plupart des langages de programmation que le flux de travail avec les fichiers est open-use-close. Pourtant, j'ai vu plusieurs fois dans les codes ruby ​​des appels File.open inégalés, et de plus j'ai trouvé ce joyau de connaissance dans la documentation ruby:

Les flux d'E / S sont automatiquement fermés lorsqu'ils sont réclamés par le garbage collector.

darkredandyellow irc aborde le problème:
[17:12] oui, et aussi, le nombre de descripteurs de fichiers est généralement limité par le système d'exploitation
[17:29] Je suppose que vous pouvez facilement manquer de descripteurs de fichiers disponibles avant que le ramasse-miettes ne nettoie vers le haut. dans ce cas, vous voudrez peut-être les fermer vous-même. "réclamé par le garbage collector." signifie que le GC agit à un moment donné dans le futur. et c'est cher. beaucoup de raisons de fermer explicitement les fichiers.

  1. Avons-nous besoin de fermer explicitement
  2. Si oui, pourquoi le GC se ferme-t-il automatiquement?
  3. Si non, pourquoi l'option?

1
Votre «connaissance commune» est dépassée depuis que les destructeurs ont été inventés.
meagar

1
@meager: Quand les destructeurs ont-ils été inventés?
Andrew Grimm

Juste une note: bien que les descripteurs de fichiers soient limités, sous Linux du moins, la limite est assez élevée.
Linuxios

1
@Linuxios: sur mon ubuntu12.04, $ ulimit -n => 1024ce n'est élevé que lorsque vous faites un travail simple. Une mauvaise habitude posera de gros problèmes un jour!
HVNSweeting

Réponses:


133

J'ai vu plusieurs fois dans les codes rubis des File.openappels inégalés

Pouvez-vous donner un exemple? Je ne vois jamais que dans le code écrit par des débutants qui n'ont pas la "connaissance commune de la plupart des langages de programmation que le flux pour travailler avec des fichiers est open-use-close".

Les rubisistes expérimentés ferment explicitement leurs fichiers ou, plus idiomatiquement, utilisent la forme de bloc de File.open, qui ferme automatiquement le fichier pour vous. Son implémentation ressemble essentiellement à ceci:

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

Les scripts sont un cas particulier. Les scripts sont généralement si courts et utilisent si peu de descripteurs de fichiers qu'il n'a tout simplement pas de sens de les fermer, car le système d'exploitation les fermera de toute façon lorsque le script se terminera.

Avons-nous besoin de fermer explicitement?

Oui.

Si oui, pourquoi le GC se ferme-t-il automatiquement?

Parce qu'après avoir collecté l'objet, il n'y a plus moyen pour vous de fermer le fichier, et ainsi vous fuiriez des descripteurs de fichier.

Notez que ce n'est pas le garbage collector qui ferme les fichiers. Le garbage collector exécute simplement tous les finaliseurs pour un objet avant de le collecter. Il se trouve que la Fileclasse définit un finaliseur qui ferme le fichier.

Si non, pourquoi l'option?

Parce que la mémoire gaspillée est bon marché, mais pas les descripteurs de fichiers gaspillés. Par conséquent, il n'est pas logique de lier la durée de vie d'un descripteur de fichier à la durée de vie d'un morceau de mémoire.

Vous ne pouvez tout simplement pas prédire quand le garbage collector s'exécutera. Vous ne pouvez même pas prédire s'il fonctionnera du tout : si vous ne manquez jamais de mémoire, le garbage collector ne fonctionnera jamais, donc le finaliseur ne fonctionnera jamais, donc le fichier ne sera jamais fermé.


1
github.com/isaac/sunspot/blob/cell/sunspot/lib/sunspot/… +23 (bien que son noyau # soit ouvert et utilisé principalement pour le côté HTTP, mais je l'ai atteint avec un paramètre de chemin de fichier local, néanmoins. ..; J'essaie toujours de trouver le temps de patcher & request-pull), github.com/jnicklas/carrierwave Ctrl + f "File.open" (c'est donné à titre d'exemple, mais dans le mauvais sens ...), et plusieurs d'autres endroits dont je ne me souviens pas. J'ai un problème avec le problème en raison des exigences de stabilité dans mes projets ..
clyfe

3
Dans cet exemple, la relance doit-elle être à l'intérieur du bloc de sauvetage? Cela ne va-t-il pas simplement générer une erreur d'exécution si lever est appelé et qu'il n'y a pas d'exception?
Jeff Storey

@JeffStorey: belle prise! 17 mois inaperçus…
Jörg W Mittag

@ JörgWMittag et maintenant 17 mois de plus ne sont pas fixés: PI suppose que le point principal ici est ensure, rescueet raisene sont pas du tout nécessaires.
KL-7 7

Je pense que vous ne pouvez pas vous en ensurepasser rescue. Et vous ne pouvez pas simplement avaler l'exception en silence, vous devez la propager à l'appelant, après avoir fermé le fichier. Quoi qu'il en soit, rappelez-moi à nouveau en mai '15 :-D
Jörg W Mittag

71

Vous devez toujours fermer les descripteurs de fichiers après utilisation, cela les videra également. Les gens utilisent souvent File.open ou une méthode équivalente avec des blocs pour gérer la durée de vie du descripteur de fichier. Par exemple:

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

Dans cet exemple, le fichier est fermé automatiquement.


Bon point. J'ai suivi un bug dans un script qui n'appelle pas File.close. En conséquence, la dernière ligne sera manquante dans certains fichiers de temps en temps.
Erwan Legrand

Exceptionnel. Je n'ai jamais connu ce truc. Un peu comme java-8 à cet égard. Je vous remercie.
sagneta

2

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

Sans bloc associé, File.open est synonyme de :: new. Si le bloc de code optionnel est donné, le fichier ouvert lui sera transmis comme argument et l'objet File sera automatiquement fermé à la fin du bloc. La valeur du bloc sera renvoyée par File.open.

Par conséquent, sera automatiquement fermé lorsque le bloc se termine : D


1
  1. Oui
  2. Au cas où vous ne le faites pas, ou s'il y a un autre échec
  3. Voir 2.

-3

Nous pouvons utiliser la File.read()fonction pour lire le fichier en ruby ​​..... comme,

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

dans cet exemple file_variablepeut avoir la valeur complète de ce fichier ....

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.