2016-10-01 17 views
0

我需要读取,修改并更新相同功能中的某些文件。理想的解决方案(见下文)无法工作,这是错误的。 “最差”解决方案有效。最短的代码来读取文件,然后更新它?

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

我希望避免2010年“简单而丑陋的解决方法”,由里德·波顿建议:

doit file = do 
    contents <- readFile file 
    length contents `seq` (writeFile file $ process contents) 

有没有更好的解决办法?

+0

那么,你可以有一个管道/管道解决方案。 :) – Sibi

+0

@Sibi这些库很大。我认为这需要很长时间才能掌握。另外,我不认为用大炮杀死蚊子。 –

+1

@AlbertoCapitani你不需要大炮来杀死蚊子 - 但是你也不需要超级计算机(按70年代的标准)拨打电话。 – leftaroundabout

回答

1

ideal的问题在于它会懒散地读取字符串,即文件在内存中未完全存在,而您已经尝试再次打开该文件进行写入。

这种lazyness的是现在被广泛认为是一个坏主意 - 如果你真的需要这样读的,你去的能力,然后conduit/pipes是你想要的。

在你的例子中,你并不需要懒惰,尽管尽管如此,除非该文件太大而不能保证一次在内存中。所以,你可以只使用readFile,但需要使它严格:一个手动的方式做这将是

ideal = do 
    fc <- readFile pathFile 
    length fc `seq` writeFile pathFile (fc++ "!") 
where pathFile = "C:\\TEMP\\Foo.txt" 

在这里,我用length,以确保该字符串是真正评估到最后。在保证同样的事情,一个更好的方法是使用deepseq

ideal = do 
    fc <- readFile pathFile 
    fc `deepseq` writeFile pathFile (fc++ "!") 

...或者,如果你想将它指向自由,

ideal = readFile pathFile >>= (writeFile pathFile . (++ "!") $!!) 

注意的readFile更高效更现代的变种类型比String - 特别是,Data.Text.readFile - 不需要这些,因为它们严格的开箱即用。因此,以下只是工作,而且可能是最好的解决办法:

{-# 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”的最早期实际上是基于lazyness,因此老的库有些淹没了它。

相关问题