gpt4 book ai didi

Haskell 惰性 I/O 和关闭文件

转载 作者:行者123 更新时间:2023-12-03 12:04:03 25 4
gpt4 key购买 nike

我编写了一个小型 Haskell 程序来打印当前目录中所有文件的 MD5 校验和(递归搜索)。基本上是 md5deep 的 Haskell 版本.一切都很好,除非当前目录有大量文件,在这种情况下我会收到如下错误:

<program>: <currentFile>: openBinaryFile: resource exhausted (Too many open files)

似乎 Haskell 的懒惰导致它不关闭文件,即使在其相应的输出行完成之后也是如此。

相关代码如下。感兴趣的函数是 getList .
import qualified Data.ByteString.Lazy as BS

main :: IO ()
main = putStr . unlines =<< getList "."

getList :: FilePath -> IO [String]
getList p =
let getFileLine path = liftM (\c -> (hex $ hash $ BS.unpack c) ++ " " ++ path) (BS.readFile path)
in mapM getFileLine =<< getRecursiveContents p

hex :: [Word8] -> String
hex = concatMap (\x -> printf "%0.2x" (toInteger x))

getRecursiveContents :: FilePath -> IO [FilePath]
-- ^ Just gets the paths to all the files in the given directory.

关于如何解决这个问题有什么想法吗?

整个程序可在此处获得: http://haskell.pastebin.com/PAZm0Dcb

编辑:我有很多文件不适合 RAM,所以我不是在寻找一种将整个文件一次读入内存的解决方案。

最佳答案

你不需要使用任何特殊的方式来做IO,你只需要改变你做事的顺序。因此,不是打开所有文件然后处理内容,而是打开一个文件并一次打印一行输出。

import Data.Digest.Pure.MD5 (md5)
import qualified Data.ByteString.Lazy as BS

main :: IO ()
main = mapM_ (\path -> putStrLn . fileLine path =<< BS.readFile path)
=<< getRecursiveContents "."

fileLine :: FilePath -> BS.ByteString -> String
fileLine path c = hash c ++ " " ++ path

hash :: BS.ByteString -> String
hash = show . md5

顺便说一句,我碰巧使用了不同的 md5 哈希库,差异并不显着。

这里发生的主要事情是这一行:
mapM_ (\path -> putStrLn . fileLine path =<< BS.readFile path)

它打开一个文件,消耗文件的全部内容并打印一行输出。它关闭文件,因为它正在消耗文件的全部内容。以前,您在文件被消耗时延迟,而在文件关闭时延迟。

如果您不确定您是否正在使用所有输入但又想确保文件无论如何都被关闭,那么您可以使用 withFile函数来自 System.IO :
mapM_ (\path -> withFile path ReadMode $ \hnd -> do
c <- BS.hGetContents hnd
putStrLn (fileLine path c))
withFile函数打开文件并将文件句柄传递给正文函数。它保证当正文返回时文件被关闭。这种“withBlah”模式在处理昂贵的资源时非常常见。 System.Exception.bracket 直接支持此资源模式.

关于Haskell 惰性 I/O 和关闭文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2981582/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com