3 番目のステートメントは正しいですか?
「最後の文字が読み取られるとすぐに」、ファイルは閉じられません。少なくとも通常はそうではありませんが、読み取り中にあった半閉じた状態のまま残ります。IO-manager/runtime次にそのようなアクションを実行するときに閉じます。ファイルをすばやく開いて読み取る場合、OS の制限が高すぎなければ、そのラグによってファイル ハンドルが不足する可能性があります。
ただし、ほとんどの使用例 (私の限られた経験では) では、ファイル ハンドルを閉じるだけで十分です。[レイジー IO はすべての場合において非常に危険であると考え、同意しない人がいます。確かに落とし穴がありますが、IMO の危険性はしばしば誇張されています。]
readFileでは、自分でファイル ハンドルを閉じずに呼び出すことはできますか?
はい、 を使用しreadFileている場合、ファイルの内容が完全に読み取られた場合、またはファイル ハンドルが参照されなくなったことに気付いた場合、ファイル ハンドルは自動的に閉じられます。
結果文字列全体を消費 (訪問) しない限り、ハンドルは開いたままになりますか?
完全ではありませんreadFile。ファイル ハンドルを半閉じた状態にしhGetContentsます。
計算により、hGetContents hdlによって管理されるチャネルまたはファイルの未読部分に対応する文字のリストが返されますhdl。これは中間状態であるセミクローズに置かれます。この状態では、hdlは事実上閉じられていますが、アイテムはhdlオンデマンドで読み取られ、 によって返される特別なリストに蓄積されます。hGetContents hdl.
foo :: String -> IO String
foo filename = do
s <- readFile "t.txt"
putStrLn "File has been read."
return s
ああ、それは反対側の遅延 IO の落とし穴の 1 つです。ここでは、内容が読み取られる前にファイルが閉じられます。戻るとfoo、ファイル ハンドルは参照されなくなり、閉じられます。foos resultのコンシューマーは、それsが空の文字列であることを検出します。これhGetContentsは、ファイルから実際に読み取ろうとすると、ハンドルが既に閉じられているためです。
の振る舞いと の振る舞いを混同しreadFileた
bracket (openFile file ReadMode) hClose hGetContents
そこの。が参照されなくreadFileなった後にのみファイルハンドルを閉じるため、ここでは期待どおりに正しく動作します。s
が実行されるときputStrLn、私は(直感的に)それを期待します
sfile の全コンテンツを含みますt.txt。
- ファイルの読み取りに使用されたハンドルが閉じられました。
いいえ、sファイルハンドルからいくつかの文字を取得するためのレシピ以外にはまだ何も含まれていません。ファイル ハンドルは半分閉じていますが、閉じていません。ファイルの内容が完全に読み取られるかs、範囲外になると閉じられます。
そうでない場合:
- 実行時に何が
s含まputStrLnれますか?
- 実行時のファイルハンドルの状態は
putStrLn?
putStrLn実行時sにファイルの内容全体が含まれていない場合、この内容が実際に読み取られるのはいつで、ファイルはいつ閉じられますか?
最初の 2 つの質問には回答済みで、3 番目の質問に対する回答は「コンテンツが消費されたときにファイルが読み込まれる」であり、コンテンツ全体が読み込まれるか、参照されなくなったときに閉じられます。
上記のbracket呼び出しとは異なります。他のアクションが例外をスローした場合でもbracket、最終操作 (ここでは )hCloseが実行されることが保証されるため、多くの場合、その使用が推奨されます。ただし、が戻っhCloseたときに が実行され、 が実際に閉じられたファイル ハンドルからコンテンツを取得できなくなります。ただし、例外が発生した場合、必ずしもファイル ハンドルを閉じるとは限りません。brackethGetContentsreadFile
これは、遅延 IO の危険性または癖の 1 つです。ファイルは、その内容が要求されるまで読み取られません。遅延 IO を間違って使用すると、手遅れになり、内容を取得できなくなります。
これは、多くの (またはほとんどの) 人が何度か陥る罠ですが、それに噛まれた後は、IO を非遅延にする必要がある場合をすぐに学習し、そのような場合は非遅延で実行します。
代替手段 (反復子、列挙子、コンジット、パイプなど) は、[実装者が間違いを犯さない限り] これらのトラップを回避しますが、遅延 IO が完全に問題ない場合に使用するのはあまり適切ではありません。その一方で、彼らは怠惰が望まれないケースをより良く扱います。