Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell withFile与openFile_Haskell - Fatal编程技术网

Haskell withFile与openFile

Haskell withFile与openFile,haskell,Haskell,当给定一个由以下分隔符分隔的文本输入文件时,此程序将生成预期的输出: import System.IO main :: IO () main = do h <- openFile "test.txt" ReadMode xs <- getlines h sequence_ $ map putStrLn xs getlines :: Handle -> IO [String] getlines h = hGetContents h &

当给定一个由以下分隔符分隔的文本输入文件时,此程序将生成预期的输出:

import System.IO

main :: IO ()
main = do h <- openFile "test.txt" ReadMode 
          xs <- getlines h
          sequence_ $ map putStrLn xs

getlines :: Handle -> IO [String]
getlines h = hGetContents h >>= return . lines

文件关闭太早。从:

从withFile退出时,句柄将关闭

这意味着
withFile
函数返回后,文件将立即关闭

因为
hGetContents
和friends都是懒惰的,所以在使用
putStrLn
强制之前,它不会尝试读取该文件,但到那时,
withFile
将已经关闭该文件

要解决此问题,请将整个内容传递到
with file

main = withFile "test.txt" ReadMode $ \handle -> do
           xs <- getlines handle
           sequence_ $ map putStrLn xs
main=withFile“test.txt”ReadMode$\handle->do

xs他们做完全不同的事情。
openFile
打开文件并返回文件句柄:

openFile :: FilePath -> IOMode -> IO Handle
withFile
用于包装接受文件句柄的IO计算,以确保句柄随后关闭:

withFile :: FilePath -> IOMode -> (Handle -> IO r) -> IO r
在您的情况下,使用withFile如下所示:

main = withFile "test.txt" ReadMode $ \h -> do
      xs <- getlines h
      sequence_ $ map putStrLn xs
main=withFile“test.txt”ReadMode$\h->do

xs您正遇到懒惰IO的常见障碍。。。lazy IO听起来是一个很好的主意,在你开始遇到这些可怕的问题之前,让流媒体变得很快

并不是说你的特殊情况对一个有经验的Haskeller来说不会是一个危险因素:这是一个教科书上的例子,说明了为什么懒惰IO是一个问题

main = do xs <- withFile "test.txt" ReadMode getlines
          sequence_ $ map putStrLn xs

正如其他人所指出的,
hGetContents
是懒惰的。但是,如果您愿意,您可以增加严格性:

import Control.DeepSeq

forceM :: (NFData a, Monad m) => m a -> m a
forceM m = do
  val <- m
  return $!! val

main = do xs <- withFile "text.txt" ReadMode (forceM . getlines)
          ...
import Control.DeepSeq
力m::(NFData,单子m)=>MA->MA
力m=do

valUgh,难道没有人给出简单的解决方案吗

main :: IO ()
main = do xs <- fmap lines $ readFile "test.txt"
          mapM_ putStrLn xs
main::IO()

main=do xs在我寻找答案时,给你一个不相关的提示:
sequence\ux。map
可以更容易地写成
mapM
。另一个不相关的提示:
foo>>=return。bar
写得更好
fmap bar foo
;我特别喜欢中缀fmap的同义词:
bar foo
(需要
导入控制。Applicative
)应该是
xs,而不是所有IO在Haskell中都是懒惰的。只需实现这样的IO,其中包括
hGetContents
。另请参见:您是否检查了它是否实际工作?我看到两个潜在的问题:1。实际上不一定是问题,但如果您使用
Control.Exception.evaluate
而不是
$,则可以更容易地推断何时对IO进行评估,和2。更严重的问题,
$
最多只能计算val的第一个构造函数,因此您只能获取一行;你真的想要更实质的东西,比如deepSeq,或者更好的非懒惰IO。诚然,我没有检查它/不工作/工作,但我怀疑:)@benmachine我检查过(使用OP给出的其余代码),它确实工作(我在一个有5行的小测试文件上检查了它)。它还可以使用
(evaluate@benmachine您对大文件的怀疑是正确的;
bash>wc-l test.txt
=
54730 test.txt
bash>runhaskell-lazyio.hs;wc-l
=
228
。奇怪的是,我认为
LineBuffering
是默认值。用DeepSeq的
$测试:
bash>runhaskell-lazyio.hs
。)>=
54731
IIRC,Unix默认为终端的行缓冲,但为文件的块缓冲。实际上,您也不能关闭文件:)如果您不小心,您将以这种方式用完文件句柄。无论如何,如果您想做更高级的事情(如查找、设置缓冲模式或其他)然后
使用file
是一种方法。我不相信解决一个比询问者要求的更一般的问题。你可以做一个任务的复杂程度是没有限制的,所以这种方法是疯狂的。只要解决你面前的任务就行了。这里没有迹象表明OP想要用特殊模式或OP来查找或打开文件不止一个文件。
readFile
是一种每天都很方便的函数,我经常看到人们滚动自己的复杂代码来读取文件,可能是因为他们习惯了不提供与
readFile
等效的单一函数的语言。但问题不是“我如何读取文件?”而是“我如何使用
with file
?”或者更具体地说,“为什么
with file
的行为如此令人惊讶?”。我认为期望这样一个问题也能详细解释提问者为什么需要使用
with file
,这是很愚蠢的。有很多很好的理由。其他答案解决了这个问题。”为什么我的程序表现得如此令人惊讶",这一切都很好。然后他们都继续以各种复杂的方式重新实现OP的程序。我只是发现一个奇怪的遗漏,没有人提到简单的2行程序方法来实现OP想要做的事情。我很困惑。这样调用
readFile
会留下一个打开的文件句柄吗?
withFile
的吸引力似乎很强它将清理在块末尾分配的所有资源。
main = withFile "test.txt" ReadMode$ \h -> do
         xs <- getlines h
         mapM_ putStrLn xs
import Control.DeepSeq

forceM :: (NFData a, Monad m) => m a -> m a
forceM m = do
  val <- m
  return $!! val

main = do xs <- withFile "text.txt" ReadMode (forceM . getlines)
          ...
main :: IO ()
main = do xs <- fmap lines $ readFile "test.txt"
          mapM_ putStrLn xs