2016-10-01 2 views
0

J'ai besoin de lire, modifier et ensuite mettre à jour certains fichiers dans la même fonction. La solution idéale (voir ci-dessous) ne pourrait pas fonctionner, c'est faux. La "pire" solution fonctionne.Le code le plus court pour lire un fichier, puis le mettre à jour?

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

Je préférerais éviter "la solution de contournement simple mais laid" proposée par Reid Burton en 2010:

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

est-il une meilleure solution?

+0

Eh bien, vous pouvez avoir une solution de conduits/tuyaux. :) – Sibi

+0

@Sibi Ces bibliothèques sont énormes. Je pense que cela demande beaucoup de temps pour être maîtrisé. Aussi, je ne pense pas à utiliser un canon pour tuer un moustique. –

+1

@AlbertoCapitani vous n'avez pas besoin d'un canon pour tuer un moustique - mais vous n'avez pas non plus besoin d'un superordinateur (selon les normes des années 70) pour faire des appels téléphoniques. – leftaroundabout

Répondre

1

Le problème avec ideal est qu'il lit la chaîne paresseusement, c'est-à-dire que le fichier n'est pas entièrement en mémoire avant que vous n'essayiez déjà de l'ouvrir à nouveau pour l'écriture.

Ce genre de lazyness est maintenant largement considéré comme une mauvaise idée - si vous avez vraiment besoin d'une telle lecture as-you-go capacités, puis conduit/pipes est ce que vous voulez. Cependant, dans votre exemple, vous n'avez pas vraiment besoin de lazyness au, sauf si le fichier est trop volumineux pour justifier l'avoir en mémoire en une seule fois. Ainsi, vous pouvez simplement utiliser readFile, mais que vous devez faire stricte: un moyen manuel pour le faire serait

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

Ici, je l'ai utilisé length pour assurer la chaîne est vraiment évaluée à la fin. Une agréable façon d'assurer la même chose est avec deepseq:

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

... ou, si vous le voulez point libre,

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

Notez que les variantes plus modernes de readFile avec plus efficace types que String - en particulier, Data.Text.readFile - ne pas besoin de tout cela, car ils sont rigoureux hors de la boîte. Ce qui suit fonctionne exactement, et est probablement la meilleure solution:

{-# 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" 

Dans les premiers jours de Haskell, tout « entrelacée IO » était en fait basé sur lazyness, d'où les anciennes bibliothèques ont été quelque peu noyés avec.