文件I / O无法生成输出的困难

时间:2012-03-17 21:18:34

标签: haskell

我正在学习Haskell,我正在尝试编写一些只读取文件并使用lines函数创建行列表的代码。例如,我有一个名为data.txt的文件,其中包含以下行:

this is line one
another line
and the final line

以下是我尝试将此数据读入列表并将其打印到屏幕上的代码:

import System.IO  
import Control.Monad

main = do  
        let list = []
        handle <- openFile "data.txt" ReadMode
        contents <- hGetContents handle
        let myLines = lines contents
            list = listLines myLines
        print list
        hClose handle   

listLines :: [String] -> [String]
listLines = map read

生成的代码编译,但不生成任何输出。我得到以下输出:

runhaskell test.hs        
read_file.hs: Prelude.read: no parse

任何人都可以帮我理解我的代码有什么问题吗?感谢。

2 个答案:

答案 0 :(得分:8)

您可能已经注意到,错误消息告诉您read存在问题,所以让我们关注它。

正如我在评论中所说,read采用某种数据类型值的字符串表示形式,尝试解析它并返回该值。一些例子:

read "3.14"    :: Double ≡ 3.14    :: Double
read "'a'"     :: Char   ≡ 'a'     :: Char
read "[1,2,3]" :: [Int]  ≡ [1,2,3] :: [Int]

一些非实例:

read "[1,2," :: [Int] ≡ error "*** Exception: Prelude.read: no parse"
read "abc"   :: Int   ≡ error "*** Exception: Prelude.read: no parse"

当您尝试使用Stringread(即read :: String → String)时会发生什么?

Haskell对String的表示(例如,当您评估GHCi中返回String的内容时)由" ... "引号括起来的一系列字符组成。当然,如果你想显示一些特殊字符(比如换行符),你必须将转义版本放在那里(在这种情况下为\n)。

还记得当我写read期望值的字符串表示时?在您的情况下,read期望完全此字符串格式。当然,它尝试做的第一件事就是匹配开头的报价。由于您的第一行 不以"开头,read会抱怨并崩溃该计划。

read "hello" :: String失败的方式与read "1" :: [Int]失败的方式相同;单独1无法解析为Int s的列表 - read期望字符串以开括号[开头。


你可能也听说过show read,这是read的反向(但在非常松散意义上)。根据经验,如果您想x值为read,则字符串表示show x的格式应为"this is line one" "another line" "and the final line"


如果您要将文件内容更改为以下

["this is line one","another line","and the final line"]

您的代码可以正常工作并产生以下输入:

.txt

如果您不想更改list = listLines myLines文件,只需删除print myLines并执行["this is line one","another line","and the final line"] 即可。但是,当您运行该程序时,您将获得

print = putStrLn ∘ show

一次。那么问题是什么?

show以及show [a]某些内容列表的默认行为(例如a Char; [ firstElement , secondElement ... lastElement ]除外获得特殊处理的{1}}是生成字符串[ ... ]。如您所见,如果您想避开[String],则必须将unlines合并在一起。

有一个名为lines的漂亮函数,它是print的反函数。另请注意,show首先调用putStrLn,但在这种情况下我们不希望这样(我们已经获得了我们想要的字符串)!所以我们使用main = do handle <- openFile "data.txt" ReadMode contents <- hGetContents handle let myLines = lines contents putStrLn (unlines myLines) hClose handle ,我们就完成了。最终版本:

lines ~ unlines

我们还可以摆脱不必要的putStrLn contents和{{1}}。

答案 1 :(得分:2)

hGetContents handle将句柄的内容读入字符串。要将所述字符串转换为行列表,您真的希望将其拆分为换行符。你不需要处理read - 你只需要字符串。所以你只需要lines :: String -> [String]。此外,您的代码似乎表明您正在“强制性地思考”,在阅读之前“初始化”list。尝试一下

的内容
main = do 
  handle <- openFile "data.txt" ReadMode
  contents <- hGetContents handle 
  let list = lines contents
  print list
  hClose handle

但是,有一些方法可以使您的程序更简单。如果您实际上不需要文件句柄来处理其他内容,只需调用readFile :: FilePath -> IO String即可轻松获取文件中的所有文本。如果你不需要list实际绑定到一个名字,你也可以摆脱它,将你的程序减少到

main = do
  contents <- readFile "data.txt"
  print (lines contents)

回顾一下这里发生的事情:读取“data.txt”,其整个内容可用contents :: String。您希望将此字符串转换为字符串列表,每行一个字符串,即lines contents确切的字符串。