当我在Haskell中打开一个文件进行读取时,我发现在关闭文件后我无法使用该文件的内容.例如,该程序将打印文件的内容:
main = do inFile <- openFile "foo" ReadMode contents <- hGetContents inFile putStr contents hClose inFile
我期望将putStr
线与hClose
线交换将没有任何效果,但此程序不打印任何内容:
main = do inFile <- openFile "foo" ReadMode contents <- hGetContents inFile hClose inFile putStr contents
为什么会这样?我猜这与懒惰评估有关,但我认为这些表达式会被排序,所以不会出现问题.你会如何实现这样的功能readFile
?
正如其他人所说,这是因为懒惰的评价.此操作后手柄处于半关闭状态,并在读取所有数据时自动关闭.hGetContents和readFile都以这种方式变得懒惰.如果您遇到手柄处于打开状态的问题,通常只需强制读取即可.这是简单的方法:
import Control.Parallel.Strategies (rnf) -- rnf means "reduce to normal form" main = do inFile <- openFile "foo" contents <- hGetContents inFile rnf contents `seq` hClose inFile -- force the whole file to be read, then close putStr contents
然而,现在,没有人使用字符串进行文件I/O. 新方法是在需要延迟读取时使用Data.ByteString(在hackage上可用)和Data.ByteString.Lazy .
import qualified Data.ByteString as Str main = do contents <- Str.readFile "foo" -- readFile is strict, so the the entire string is read here Str.putStr contents
ByteStrings是获取大字符串(如文件内容)的方法.它们比String(= [Char])更快,内存效率更高.
笔记:
我只是为了方便从Control.Parallel.Strategies导入了rnf.你可以很容易地自己写一些类似的东西:
forceList [] = () forceList (x:xs) = forceList xs
这只会强制遍历列表的主干(而不是值),这将具有读取整个文件的效果.
懒惰的I/O被专家们视为邪恶; 我建议暂时对大多数文件I/O使用严格的字节串.烤箱中有一些解决方案试图带回可组合的增量读数,其中最有希望的是由Oleg称为"Iteratee".