Ruby's File.open und die Notwendigkeit für f.close

91

In den meisten Programmiersprachen ist allgemein bekannt, dass der Ablauf für die Arbeit mit Dateien Open-Use-Close ist. Dennoch habe ich viele Male in Ruby-Codes unübertroffene File.open-Aufrufe gesehen, und außerdem habe ich dieses Juwel des Wissens in den Ruby-Dokumenten gefunden:

E / A-Streams werden automatisch geschlossen, wenn sie vom Garbage Collector beansprucht werden.

darkredandyellow freundliche irc nehmen das Problem auf:
[17:12] Ja, und außerdem ist die Anzahl der Dateideskriptoren normalerweise durch das Betriebssystem begrenzt.
[17:29] Ich gehe davon aus, dass Ihnen die verfügbaren Dateideskriptoren leicht ausgehen können, bevor der Garbage Collector bereinigt wird oben. In diesem Fall möchten Sie sie möglicherweise selbst schließen. "behauptet vom Müllsammler." bedeutet, dass der GC irgendwann in der Zukunft handelt. und es ist teuer. viele Gründe für das explizite Schließen von Dateien.

  1. Müssen wir explizit schließen?
  2. Wenn ja, warum schließt der GC dann automatisch?
  3. Wenn nicht, warum dann die Option?
Clyfe
quelle
1
Ihr 'allgemeines Wissen' ist seit der Erfindung der Destruktoren veraltet.
Meagar
1
@meager: Wann wurden Destruktoren erfunden?
Andrew Grimm
Nur eine Anmerkung: Während Dateideskriptoren begrenzt sind, ist das Limit zumindest unter Linux ziemlich hoch.
Linuxios
1
@Linuxios: Auf meinem Ubuntu12.04 ist $ ulimit -n => 1024es nur dann hoch, wenn Sie nur einen einfachen Job machen. Schlechte Angewohnheit wird eines Tages ein großes Problem verursachen!
HVNSweeting

Antworten:

132

Ich habe viele Male in Ruby-Codes unübertroffene File.openAnrufe gesehen

Kannst du ein Beispiel geben? Ich sehe immer nur, dass in Code, der von Neulingen geschrieben wurde, denen das "allgemeine Wissen in den meisten Programmiersprachen fehlt, dass der Fluss für die Arbeit mit Dateien offen ist, geschlossen ist".

Erfahrene Rubyisten schließen entweder explizit ihre Dateien oder verwenden idiomatischer die Blockform von File.open, mit der die Datei automatisch für Sie geschlossen wird. Die Implementierung sieht im Grunde ungefähr so ​​aus:

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

Skripte sind ein Sonderfall. Skripte werden im Allgemeinen so kurz ausgeführt und verwenden so wenige Dateideskriptoren, dass es einfach nicht sinnvoll ist, sie zu schließen, da das Betriebssystem sie ohnehin schließt, wenn das Skript beendet wird.

Müssen wir explizit schließen?

Ja.

Wenn ja, warum schließt der GC dann automatisch?

Denn nachdem das Objekt erfasst wurde, können Sie die Datei nicht mehr schließen, sodass Sie Dateideskriptoren verlieren.

Beachten Sie, dass es nicht der Garbage Collector ist, der die Dateien schließt. Der Garbage Collector führt einfach alle Finalizer für ein Objekt aus, bevor er es sammelt. Es ist einfach so, dass die FileKlasse einen Finalizer definiert, der die Datei schließt.

Wenn nicht, warum dann die Option?

Weil verschwendeter Speicher billig ist, verschwendete Dateideskriptoren jedoch nicht. Daher ist es nicht sinnvoll, die Lebensdauer eines Dateideskriptors an die Lebensdauer eines Speicherbereichs zu binden.

Sie können einfach nicht vorhersagen, wann der Garbage Collector ausgeführt wird. Sie können nicht einmal vorhersagen, ob es überhaupt ausgeführt wird : Wenn Ihnen nie der Speicher ausgeht, wird der Garbage Collector niemals ausgeführt, daher wird der Finalizer niemals ausgeführt, daher wird die Datei niemals geschlossen.

Jörg W Mittag
quelle
1
github.com/isaac/sunspot/blob/cell/sunspot/lib/sunspot/… +23 (obwohl sein Kernel # geöffnet ist und hauptsächlich für die HTTP-Seite verwendet wird, aber ich habe ihn trotzdem mit einem lokalen Dateipfadparameter erreicht. ..; Ich versuche immer noch, Zeit zum Patchen und Anfordern-Ziehen zu finden), github.com/jnicklas/carrierwave Strg + f "File.open" (es wird als Beispiel angegeben, aber auf schlechte Weise ...) und mehrere andere Orte, an die ich mich nicht erinnere. Ich habe ein Rindfleisch mit dem Problem wegen der Stabilitätsanforderungen in meinen Projekten.
Clyfe
3
Sollte sich in diesem Beispiel die Erhöhung innerhalb des Rettungsblocks befinden? Wird dies nicht einfach einen Laufzeitfehler auslösen, wenn Raise aufgerufen wird und es keine Ausnahme gibt?
Jeff Storey
@ JeffStorey: schöner Fang! 17 Monate unbemerkt…
Jörg W Mittag
@ JörgWMittag und jetzt 17 Monate nicht festgelegt: PI denke hier die Hauptsache ist ensure, rescueund raiseist überhaupt nicht notwendig.
KL-7 7.
Ich denke, Sie können nicht ensureohne haben rescue. Und Sie können die Ausnahme nicht einfach stillschweigend verschlucken, sondern müssen sie an den Anrufer weitergeben, nachdem Sie die Datei geschlossen haben. Wie auch immer, erinnern Sie mich noch einmal im Mai '15 :-D
Jörg W Mittag
71

Sie sollten Dateideskriptoren nach der Verwendung immer schließen, damit sie auch gelöscht werden. Häufig verwenden Benutzer File.open oder eine gleichwertige Methode mit Blöcken, um die Lebensdauer des Dateideskriptors zu verwalten. Beispielsweise:

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

In diesem Beispiel wird die Datei automatisch geschlossen.

Tonttu
quelle
Guter Punkt. Ich habe einen Fehler in einem Skript verfolgt, das File.close nicht aufruft. Infolgedessen fehlt ab und zu die letzte Zeile in einigen Dateien.
Erwan Legrand
Hervorragend. Ich habe diesen Trick nie gekannt. Ähnlich wie Java-8 in dieser Hinsicht. Danke dir.
Sagneta
2

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

Ohne zugeordneten Block ist File.open ein Synonym für :: new. Wenn der optionale Codeblock angegeben ist, wird ihm die geöffnete Datei als Argument übergeben und das Dateiobjekt wird automatisch geschlossen, wenn der Block beendet wird. Der Wert des Blocks wird von File.open zurückgegeben.

Daher wird automatisch geschlossen, wenn der Block endet : D.

skozz
quelle
1
  1. Ja
  2. Falls Sie dies nicht tun oder wenn ein anderer Fehler vorliegt
  3. Siehe 2.
Satya
quelle
-3

Wir können die File.read()Funktion verwenden, um die Datei in Ruby zu lesen ..... wie z.

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

In diesem Beispiel file_variablekann der vollständige Wert dieser Datei angegeben werden.

Kumar KS
quelle