Haskell IO和关闭文件 [英] Haskell IO and closing files
问题描述
$ p $
main = do inFile < - openFilefooReadMode
contents <-hGetContents inFile
putStr contents
hClose inFile
我期望交换 putStr
行与 hClose
行不起作用,但是这个程序不会打印任何内容:
main = do inFile< - openFilefooReadMode
contents< - hGetContents inFile
hClose inFile
putStr内容
为什么会发生这种情况?我猜这与懒惰评估有关,但我认为这些表达式会被排序,所以不会有问题。你将如何实现像 readFile
?
说,这是因为懒惰的评价。操作完成后,手柄半关,所有数据读取后自动关闭。 hGetContents和readFile都是这样懒的。如果您在处理开放时遇到问题,通常您只需强制读取。这是简单的方法:
pre $ import $ Control $并行策略
- rnf表示减少到正常
main = do inFile< - openFilefoo
contents< - hGetContents inFile
rnf contents`seq` hClose inFile - 强制读取整个文件,然后关闭
putStr内容
然而,现在没有人使用字符串作为文件I / O了。新的方法是使用Data.ByteString(在hackage上可用)和Data.ByteString.Lazy,当你想读取懒惰的读取时。
导入限定的Data.ByteString为Str
main =做内容< - Str.readFilefoo
- readFile是严格的,所以整个字符串读在这里
Str.putStr内容
ByteStrings是去大字符串(如文件内容)。它们比字符串(= [Char])快得多并且内存效率更高。
注释:
我只是为了方便而从Control.Parallel.Strategies中导入了rnf。你可以很容易地写出类似的东西:
forceList [] =()
forceList(x:xs) = forceList xs
这只是强制遍历列表(而不是列表的值)会有读取整个文件的效果。
懒惰I / O被专家视为邪恶;我建议暂时使用严格的字节串作为大部分的文件I / O。在烤箱中有几个解决方案试图带回可组合的增量式读取,其中最有希望的是由Oleg称之为Iteratee。
When I open a file for reading in Haskell, I've found that I can't use the contents of the file after closing it. For example, this program will print the contents of a file:
main = do inFile <- openFile "foo" ReadMode
contents <- hGetContents inFile
putStr contents
hClose inFile
I expected that interchanging the putStr
line with the hClose
line would have no effect, but this program prints nothing:
main = do inFile <- openFile "foo" ReadMode
contents <- hGetContents inFile
hClose inFile
putStr contents
Why does this happen? I'm guessing it has something to do with lazy evaluation, but I thought these expressions would get sequenced so there wouldn't be a problem. How would you implement a function like readFile
?
As others have stated, it is because of lazy evaluation. The handle is half-closed after this operation, and will be closed automatically when all data is read. Both hGetContents and readFile are lazy in this way. In cases where you're having issues with handles being kept open, typically you just force the read. Here's the easy way:
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
These days, however, nobody is using strings for file I/O anymore. The new way is to use Data.ByteString (available on hackage), and Data.ByteString.Lazy when you want lazy reads.
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 are the way to go for big strings (like file contents). They are much faster and more memory efficient than String (= [Char]).
Notes:
I imported rnf from Control.Parallel.Strategies only for convenience. You could write something like it yourself pretty easily:
forceList [] = ()
forceList (x:xs) = forceList xs
This just forces a traversal of the spine (not the values) of the list, which would have the effect of reading the whole file.
Lazy I/O is becoming considered evil by experts; I recommend using strict bytestrings for most of file I/O for the time being. There are a few solutions in the oven which attempt to bring back composable incremental reads, the most promising of which is called "Iteratee" by Oleg.
这篇关于Haskell IO和关闭文件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!