我的第三个陈述正确吗?
不完全,文件没有关闭“一旦最后一个字符被读取”,至少通常不会,它在读取期间处于半关闭状态,IO-manager/runtime下次执行此类操作时将关闭它。如果您正在快速打开和读取文件,如果操作系统限制不是太高,那么该延迟可能会导致您用完文件句柄。
然而,对于大多数用例(以我有限的经验)来说,关闭文件句柄是足够及时的。[有些人不同意,认为惰性 IO 在所有情况下都极其危险。它肯定有陷阱,但 IMO 的危险往往被夸大了。]
那么,我可以自己调用readFile而不关闭文件句柄吗?
是的,当您使用readFile时,当文件内容被完全读取或注意到文件句柄不再被引用时,文件句柄会自动关闭。
只要我没有消耗(访问)整个结果字符串,句柄会保持打开状态吗?
不完全是,readFile将文件句柄置于半关闭状态,在文档中进行了描述hGetContents:
计算hGetContents hdl返回对应于 管理的通道或文件的未读部分的字符列表,将hdl其置于中间状态,半关闭。在这种状态下,实际上是关闭的,但项目会按需hdl读取并累积在由返回的特殊列表中hdlhGetContents 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
putStrLnis executeds不包含文件的全部内容,什么时候会真正读取这个内容,什么时候会关闭文件?
前两个问题已经回答了,第三个问题的答案是“内容被消费时会读取文件”,当内容全部被读取或不再被引用时会关闭。
这与上面的bracket调用不同 -bracket保证最终操作,hClose即使其他操作抛出异常也会运行,因此通常建议使用它。但是,返回hClose时运行bracket,然后hGetContents无法从现在真正关闭的文件句柄中获取任何内容。但readFile如果发生异常,不一定会关闭文件句柄。
这是惰性 IO 的危险或怪癖之一,文件在需要其内容之前不会被读取,如果你错误地使用惰性 IO,那就太迟了,你不会得到任何内容。
这是一个很多人(甚至大多数人)一次又一次陷入的陷阱,但是在被它咬伤之后,人们很快就会知道什么时候 IO 需要非懒惰并在这些情况下非懒惰地去做。
替代方案(迭代器、枚举器、管道、管道......)避免了这些陷阱[除非实现者犯了错误],但在惰性 IO 完全没问题的情况下使用起来就不太好用了。另一方面,他们更好地处理不希望懒惰的情况。