我的第三个陈述正确吗?
不完全,文件没有关闭“一旦最后一个字符被读取”,至少通常不会,它在读取期间处于半关闭状态,IO-manager/runtime下次执行此类操作时将关闭它。如果您正在快速打开和读取文件,如果操作系统限制不是太高,那么该延迟可能会导致您用完文件句柄。
然而,对于大多数用例(以我有限的经验)来说,关闭文件句柄是足够及时的。[有些人不同意,认为惰性 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 的陷阱之一。此处文件在其内容被读取之前关闭。返回时foo
,不再引用文件句柄,然后关闭。s 结果的使用者foo
然后会发现它s
是一个空字符串,因为当hGetContents
尝试从文件中实际读取时,句柄已经关闭。
readFile
我将 的行为与的行为混淆了
bracket (openFile file ReadMode) hClose hGetContents
那里。仅在不再引用readFile
文件句柄后才关闭文件句柄,因此它的行为在此处按预期正确。s
当putStrLn
执行时,我会(直觉地)期望
s
包含文件的全部内容t.txt
,
- 用于读取文件的句柄已关闭。
不,s
除了可能从文件句柄中获取一些字符的配方外,还没有任何内容。文件句柄是半封闭的,但不是封闭的。s
当文件内容被完全读取或超出范围时,它将关闭。
如果不是这种情况:
s
执行时包含什么putStrLn
?
- 执行时文件句柄处于什么状态
putStrLn
?
- 如果 when
putStrLn
is executeds
不包含文件的全部内容,什么时候会真正读取这个内容,什么时候会关闭文件?
前两个问题已经回答了,第三个问题的答案是“内容被消费时会读取文件”,当内容全部被读取或不再被引用时会关闭。
这与上面的bracket
调用不同 -bracket
保证最终操作,hClose
即使其他操作抛出异常也会运行,因此通常建议使用它。但是,返回hClose
时运行bracket
,然后hGetContents
无法从现在真正关闭的文件句柄中获取任何内容。但readFile
如果发生异常,不一定会关闭文件句柄。
这是惰性 IO 的危险或怪癖之一,文件在需要其内容之前不会被读取,如果你错误地使用惰性 IO,那就太迟了,你不会得到任何内容。
这是一个很多人(甚至大多数人)一次又一次陷入的陷阱,但是在被它咬伤之后,人们很快就会知道什么时候 IO 需要非懒惰并在这些情况下非懒惰地去做。
替代方案(迭代器、枚举器、管道、管道......)避免了这些陷阱[除非实现者犯了错误],但在惰性 IO 完全没问题的情况下使用起来就不太好用了。另一方面,他们更好地处理不希望懒惰的情况。