我需要读取,修改然后更新同一功能中的某些文件。理想的解决方案(见下文)不起作用,这是错误的。最糟糕的"解决方案有效。
-- Ex. "Pseudocode" - Doesn't work.
ideal = let pathFile = "C:\\TEMP\\Foo.txt"
in readFile pathFile >>= writeFile pathFile . (++ "!")
-- It works.
worst = do
let pathFile = "C:\\TEMP\\Foo.txt"
h <- openFile pathFile ReadMode
cs <- hGetContents h
(temp_Foo,temp_handle) <- openTempFile pathFile
hPrint temp_handle $ cs ++ "!"
hClose temp_handle
removeFile pathFile
renameFile temp_Foo pathFile
我宁愿避免&#34;简单但丑陋的解决方法&#34; Reid Burton在2010年提出的建议:
doit file = do
contents <- readFile file
length contents `seq` (writeFile file $ process contents)
有更好的解决方案吗?
答案 0 :(得分:1)
ideal
的问题在于它懒惰地读取字符串,即在您尝试再次打开文件进行写入之前,文件未完全在内存中。
这种懒惰现在被普遍认为是一个坏主意† - 如果你确实需要这样的随时随地阅读的功能,那么conduit / {{3}是你想要的。
在你的例子中,你根本不需要lamness ,除非文件太大而不能保证一次在内存中。所以你可以使用readFile
,但需要严格要求:手动方式是
ideal = do
fc <- readFile pathFile
length fc `seq` writeFile pathFile (fc++ "!")
where pathFile = "C:\\TEMP\\Foo.txt"
这里我使用length
来确保字符串真正被评估到最后。确保同样事情的更好方法是pipes:
ideal = do
fc <- readFile pathFile
fc `deepseq` writeFile pathFile (fc++ "!")
......或者,如果你想要它没有点,
ideal = readFile pathFile >>= (writeFile pathFile . (++ "!") $!!)
请注意readFile
的更现代的变体,其类型的效率高于String
- 特别是deepseq
- 不需要任何此类变体,因为它们是严格的盒子。因此,以下工作正常,可能是最佳解决方案:
{-# LANGUAGE OverloadedStrings #-}
import Prelude hiding (readFile, writeFile)
import Data.Text.IO
import Data.Monoid ((<>))
main :: IO ()
main = readFile pathFile >>= writeFile pathFile . (<> "!")
where pathFile = "/tmp/foo.txt"
† 在Haskell的早期阶段,所有“交错IO”实际上都是基于懒惰,因此旧库有点淹没了它。