首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在Haskell中,我想读取一个文件,然后向其写入。我需要严格的注解吗?

在Haskell中,我想读取一个文件,然后向其写入。我需要严格的注解吗?
EN

Stack Overflow用户
提问于 2010-03-27 06:48:04
回答 4查看 7.4K关注 0票数 16

对Haskell来说还是个新手。

我想读取一个文件的内容,对它做一些可能涉及IO的操作(现在使用putStrLn ),然后将新内容写入同一个文件。

我想出了:

代码语言:javascript
复制
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

代码语言:javascript
复制
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中的操作可能会很长,我不想让文件一直处于打开状态。通常,我只希望能够获取文件内容,然后在处理该内容之前将其关闭。

我想出的解决方案如下:

代码语言:javascript
复制
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本质上是不严格的?

有没有人能推荐一种更好、更短的方法来做到这一点,同时保持我想要的语义?

谢谢!

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2010-03-28 05:11:46

第一个程序无法运行的原因是withFile在执行传递给它的IO操作后关闭了该文件。在您的示例中,IO操作是hGetContents,它不会立即读取文件,而只是在需要其内容时才读取。当您尝试打印文件的内容时,withFile已经关闭了文件,因此读取失败(静默地)。

您可以通过简单地使用readFilewriteFile来修复这个问题,而不是重复发明轮子

代码语言:javascript
复制
doit file = do
    contents <- readFile file
    putStrLn contents
    writeFile file "new content"

但是假设您希望新内容依赖于旧内容。通常,您不能简单地这样做。

代码语言:javascript
复制
doit file = do
    contents <- readFile file
    writeFile file $ process contents

因为writeFile可能会影响readFile返回的内容(请记住,它还没有实际读取文件)。或者,根据操作系统的不同,您可能无法在两个单独的句柄上打开相同的文件进行读写。简单但丑陋的解决方法是

代码语言:javascript
复制
doit file = do
    contents <- readFile file
    length contents `seq` (writeFile file $ process contents)

这将强制readFile读取整个文件并在writeFile操作开始之前将其关闭。

票数 22
EN

Stack Overflow用户

发布于 2012-05-29 23:14:02

我认为解决这个问题最简单的方法是使用严格IO:

代码语言:javascript
复制
import qualified System.IO.Strict as S
main = do
    file <- S.readFile "filename"
    writeFile "filename" file
票数 11
EN

Stack Overflow用户

发布于 2016-05-09 00:21:59

您可以复制文件句柄,对原始文件句柄执行延迟写入(到文件末尾),对另一个文件执行延迟读取。所以在附加到文件的情况下不涉及严格的注释。

代码语言:javascript
复制
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 h

GHC.IO.Handle模块提供了hDuplicate函数。

返回原始句柄的副本,并带有自己的缓冲区。但是,这两个句柄将共享一个文件指针。在复制句柄之前,刷新原始句柄的缓冲区,包括丢弃任何输入数据。

使用hSeek,您可以在读取或写入之前设置手柄的位置。

但我不确定使用"AbsoluteSeek 0“而不是"SeekFromEnd 0”进行写入,即覆盖内容的可靠性如何。通常,我会建议先写入临时文件,例如使用openTempFile (来自System.IO),然后替换原始文件。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/2527271

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档