对Haskell来说还是个新手。
我想读取一个文件的内容,对它做一些可能涉及IO的操作(现在使用putStrLn ),然后将新内容写入同一个文件。
我想出了:
doit :: String -> IO ()
doit file = do
contents <- withFile tagfile ReadMode $ \h -> hGetContents h
putStrLn contents
withFile tagfile WriteMode $ \h -> hPutStrLn h "new content"然而,由于懒惰,这是不起作用的。不打印文件内容。我找到了this post,它很好地解释了这一点。
建议的解决方案是在withFile中包含putStrLn
doit :: String -> IO ()
doit file = do
withFile tagfile ReadMode $ \h -> do
contents <- hGetContents h
putStrLn contents
withFile tagfile WriteMode $ \h -> hPutStrLn h "new content"这是可行的,但这不是我想要做的。我将最终替换putStrLn中的操作可能会很长,我不想让文件一直处于打开状态。通常,我只希望能够获取文件内容,然后在处理该内容之前将其关闭。
我想出的解决方案如下:
doit :: String -> IO ()
doit file = do
c <- newIORef ""
withFile tagfile ReadMode $ \h -> do
a <- hGetContents h
writeIORef c $! a
d <- readIORef c
putStrLn d
withFile tagfile WriteMode $ \h -> hPutStrLn h "Test"然而,我发现这篇文章很长,而且有点混乱。我认为我不应该仅仅为了获得一个值而需要一个IORef,但我需要"place“来放置文件内容。而且,如果没有writeIORef的严格注解$!,它仍然无法工作。我猜IORef本质上是不严格的?
有没有人能推荐一种更好、更短的方法来做到这一点,同时保持我想要的语义?
谢谢!
发布于 2010-03-28 05:11:46
第一个程序无法运行的原因是withFile在执行传递给它的IO操作后关闭了该文件。在您的示例中,IO操作是hGetContents,它不会立即读取文件,而只是在需要其内容时才读取。当您尝试打印文件的内容时,withFile已经关闭了文件,因此读取失败(静默地)。
您可以通过简单地使用readFile和writeFile来修复这个问题,而不是重复发明轮子
doit file = do
contents <- readFile file
putStrLn contents
writeFile file "new content"但是假设您希望新内容依赖于旧内容。通常,您不能简单地这样做。
doit file = do
contents <- readFile file
writeFile file $ process contents因为writeFile可能会影响readFile返回的内容(请记住,它还没有实际读取文件)。或者,根据操作系统的不同,您可能无法在两个单独的句柄上打开相同的文件进行读写。简单但丑陋的解决方法是
doit file = do
contents <- readFile file
length contents `seq` (writeFile file $ process contents)这将强制readFile读取整个文件并在writeFile操作开始之前将其关闭。
发布于 2012-05-29 23:14:02
我认为解决这个问题最简单的方法是使用严格IO:
import qualified System.IO.Strict as S
main = do
file <- S.readFile "filename"
writeFile "filename" file发布于 2016-05-09 00:21:59
您可以复制文件句柄,对原始文件句柄执行延迟写入(到文件末尾),对另一个文件执行延迟读取。所以在附加到文件的情况下不涉及严格的注释。
import System.IO
import GHC.IO.Handle
main :: IO ()
main = do
h <- openFile "filename" ReadWriteMode
h2 <- hDuplicate h
hSeek h2 AbsoluteSeek 0
originalFileContents <- hGetContents h2
putStrLn originalFileContents
hSeek h SeekFromEnd 0
hPutStrLn h $ concatMap ("{new_contents}" ++) (lines originalFileContents)
hClose h2
hClose hGHC.IO.Handle模块提供了hDuplicate函数。
返回原始句柄的副本,并带有自己的缓冲区。但是,这两个句柄将共享一个文件指针。在复制句柄之前,刷新原始句柄的缓冲区,包括丢弃任何输入数据。
使用hSeek,您可以在读取或写入之前设置手柄的位置。
但我不确定使用"AbsoluteSeek 0“而不是"SeekFromEnd 0”进行写入,即覆盖内容的可靠性如何。通常,我会建议先写入临时文件,例如使用openTempFile (来自System.IO),然后替换原始文件。
https://stackoverflow.com/questions/2527271
复制相似问题