39 votes

Haskell IO et la fermeture des fichiers

Quand j'ouvre un fichier pour la lecture en Haskell, j'ai constaté que je ne peux pas utiliser le contenu du fichier après sa fermeture. Par exemple, ce programme permettra d'imprimer le contenu d'un fichier:

main = do inFile <- openFile "foo" ReadMode
          contents <- hGetContents inFile
          putStr contents
          hClose inFile

Je m'attendais à ce interchanger l' putStr ligne avec l' hClose ligne aurait aucun effet, mais ce programme imprime rien:

main = do inFile <- openFile "foo" ReadMode
          contents <- hGetContents inFile
          hClose inFile
          putStr contents

Pourquoi est-ce arrivé? Je suppose qu'il a quelque chose à voir avec l'évaluation différée, mais j'ai pensé que ces expressions obtenir séquencé donc il ne serait pas un problème. Comment voulez-vous mettre en œuvre une fonction comme readFile?

38voto

luqui Points 26009

Comme d'autres l'ont dit, c'est en raison de l'évaluation différée. La poignée est à moitié fermé après cette opération, et se ferme automatiquement une fois toutes les données sont lues. Les deux hGetContents et readFile sont paresseux de cette façon. Dans le cas où vous rencontrez des problèmes avec poignées ouvertes, généralement vous venez de force de la lire. Voici le moyen le plus facile:

import Control.Parallel.Strategies (rnf)
-- rnf means "reduce to normal form"
main = do inFile <- openFile "foo" 
          contents <- hGetContents inFile
          rnf contents `seq` hClose inFile -- force the whole file to be read, then close
          putStr contents

Ces jours-ci, cependant, personne n'est à l'aide de cordes pour les e/S de fichier plus. La nouvelle façon est d'utiliser des Données.ByteString (disponible sur le hackage), et de Données.ByteString.Paresseux quand vous voulez paresseux lit.

import qualified Data.ByteString as Str

main = do contents <- Str.readFile "foo"
          -- readFile is strict, so the the entire string is read here
          Str.putStr contents

ByteStrings sont la voie à suivre pour les grandes chaînes de caractères (comme le contenu du fichier). Ils sont beaucoup plus rapide et plus efficace en terme de mémoire de la Chaîne (= [Char]).

Notes:

J'ai importé le frr de Contrôle.En parallèle.Des stratégies uniquement pour des raisons de commodité. Vous pouvez écrire quelque chose comme vous-même assez facilement:

  forceList [] = ()
  forceList (x:xs) = forceList xs

- Ce juste une des forces de la traversée de la colonne vertébrale (pas les valeurs) de la liste, ce qui aurait pour effet de la lecture du fichier.

Paresseux I/O est en train de devenir considéré comme mauvais par des experts; je recommande l'utilisation stricte bytestrings pour la plupart des e/S de fichier pour le moment. Il y a quelques solutions dans le four qui tentent de ramener composable différentiels lit, le plus prometteur de ce qui est appelé "Iteratee" par Oleg.

2voto

Erik Hesselink Points 1570

C'est parce que hGetContents ne rien faire pour le moment: il est paresseux I/O. Uniquement lorsque vous utilisez la chaîne de résultat le fichier est lu (ou la partie de celle-ci est nécessaire). Si vous voulez forcer la lecture, vous pouvez calculer sa longueur, et d'utiliser la fonction de séquenceur à la force de la longueur d'évaluation. Paresseux I/O peut être cool, mais il peut aussi être source de confusion.

Pour plus d'informations, voir la partie sur les paresseux I/O dans le Monde Réel Haskell, par exemple.

1voto

Chris Conway Points 24671

Comme indiqué précédemment, hGetContents est paresseux. readFile est stricte et ferme le fichier quand c'est fait:

main = do contents <- readFile "foo"
          putStr contents

les rendements de la suite dans les Câlins

> main
blahblahblah

foo est

blahblahblah

Il est intéressant de noter, seq ne garantit qu' une partie de l'entrée est lu, pas tout cela:

main = do inFile <- openFile "foo" ReadMode
          contents <- hGetContents $! inFile
          contents `seq` hClose inFile
          putStr contents

les rendements

> main
b

Une bonne ressource est: Faire de Haskell programmes plus rapides et les plus petits: hGetContents, hClose, readFile

1voto

Robin Green Points 12926

Si vous voulez garder votre IO paresseux, mais le faire en toute sécurité, de sorte que des erreurs comme cela ne se produit pas, l'utilisation d'un package conçu pour cette tels que coffre-paresseux-io. (Cependant, coffre-paresseux-io ne prend pas en charge bytestring I/O.)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X