读取文件然后更新的最短代码?

时间:2016-10-01 09:37:27

标签: haskell io

我需要读取,修改然后更新同一功能中的某些文件。理想的解决方案(见下文)不起作用,这是错误的。最糟糕的"解决方案有效。

-- 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)

有更好的解决方案吗?

1 个答案:

答案 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”实际上都是基于懒惰,因此旧库有点淹没了它。