当给定由\n分隔的文本输入文件时,此程序产生我期望的输出:
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
Run Code Online (Sandbox Code Playgroud)
用withFile代替openFile并稍微重新排列
import System.IO
main :: IO ()
main = do xs <- withFile "test.txt" ReadMode getlines
sequence_ $ map putStrLn xs
getlines :: Handle -> IO [String]
getlines h = hGetContents h >>= return . lines
Run Code Online (Sandbox Code Playgroud)
我设法完全没有输出.我很难过.
编辑:不再难过了:感谢一个人和所有人的深思熟虑和发人深省的答案.我在文档中做了一些阅读,并了解到withFile可以理解为支架的部分应用.
这就是我最终得到的结果:
import System.IO
main :: IO ()
main = withFile "test.txt" ReadMode $ \h -> getlines h >>= mapM_ putStrLn
getlines :: Handle -> IO [String]
getlines h = lines `fmap` hGetContents h
Run Code Online (Sandbox Code Playgroud)
Lam*_*iry 30
该文件过早关闭.从文档:
句柄将在withFile退出时关闭
这意味着withFile函数返回后文件将立即关闭.
因为hGetContents和朋友是懒惰的,它不会尝试读取文件,直到它被强制使用putStrLn,但到那时,withFile就已经关闭了文件.
要解决这个问题,请将整个内容传递给withFile:
main = withFile "test.txt" ReadMode $ \handle -> do
xs <- getlines handle
sequence_ $ map putStrLn xs
Run Code Online (Sandbox Code Playgroud)
这是有效的,因为withFile到了关闭文件的时候,你已经打印过了.
Rei*_*ton 12
呃,没有人给出简单的解决方案吗?
main :: IO ()
main = do xs <- fmap lines $ readFile "test.txt"
mapM_ putStrLn xs
Run Code Online (Sandbox Code Playgroud)
你可以使用时不要使用openFile+ hGetContents或withFile+ .随着你不能过早地关闭文件搬起石头砸自己的脚.hGetContentsreadFilereadFile
他们完全不同的事情.
openFile打开一个文件并返回一个文件句柄:
openFile :: FilePath -> IOMode -> IO Handle
Run Code Online (Sandbox Code Playgroud)
withFile 用于包装带有文件句柄的IO计算,确保句柄在之后关闭:
withFile :: FilePath -> IOMode -> (Handle -> IO r) -> IO r
Run Code Online (Sandbox Code Playgroud)
在您的情况下,使用withFile将如下所示:
main = withFile "test.txt" ReadMode $ \h -> do
xs <- getlines h
sequence_ $ map putStrLn xs
Run Code Online (Sandbox Code Playgroud)
您当前拥有的版本将打开文件,调用getlines,然后关闭该文件.由于getlines是懒惰的,因此在文件打开时不会读取任何输出,一旦文件关闭,它就不能.
你正在遇到懒惰IO的常见障碍......懒惰的IO听起来像是一个很好的主意,使得流式传输变得轻而易举,直到你开始遇到那些可怕的问题.
并不是说你的特殊情况不会是一个经验丰富的Haskeller的红色鲱鱼:这是为什么懒惰IO是一个问题的教科书示例.
main = do xs <- withFile "test.txt" ReadMode getlines
sequence_ $ map putStrLn xs
Run Code Online (Sandbox Code Playgroud)
withFile采用FilePath,模式和操作来处理使用此模式打开此文件路径所产生的句柄.有趣的部分从withFile是,它与支架和保障实施,即使在例外的情况下,比文件将在手柄上执行动作后关闭.这里的问题是有问题的行为(getLines)根本不读取文件!它只承诺在真正需要内容时这样做!这是懒惰的IO(用unsafeInterleaveIO实现,猜猜"不安全"部分意味着......).当然这个内容的时间是需要(putStrLn),手柄被withFile关闭的承诺.
所以你有几个解决方案:你可以明确地使用open和close(并放弃异常安全),或者你可以使用惰性IO,但是每个动作都会触及受withFile保护的范围内的文件内容:
main = withFile "test.txt" ReadMode$ \h -> do
xs <- getlines h
mapM_ putStrLn xs
Run Code Online (Sandbox Code Playgroud)
在这种情况下,这不是太糟糕,但如果您忽略何时需要内容,您应该会发现问题可能会变得更加烦人.懒惰的IO在一个庞大而复杂的程序中可能会很快变得非常烦人,并且当打开的句柄数量的进一步限制开始变得重要时...这就是为什么Haskell社区的新运动要想出解决流媒体内容问题的原因(而不是读取内存中的整个文件,以牺牲内存使用为代价来解决问题,而不是懒惰的IO).有一段时间,似乎Iteratee将成为标准的解决方案,但它非常复杂且难以理解,即使对于经验丰富的Haskeller,所以其他候选人最近也悄悄上升:目前最有希望或至少成功的似乎是是"管道".
| 归档时间: |
|
| 查看次数: |
6510 次 |
| 最近记录: |