我有包含这样数据的文本文件:
13.
13.
[(1,2),(2,3),(4,5)].
我想把它读成 Haskell 中的 3 个变量。但是标准函数将其读取为字符串,但考虑到我自己最后摆脱了点,是否有任何内置的解析器函数可以使整数为“13”并[(Integer,Integer)]
列出[(1,2),(2,3),(4,5)]
?
我有包含这样数据的文本文件:
13.
13.
[(1,2),(2,3),(4,5)].
我想把它读成 Haskell 中的 3 个变量。但是标准函数将其读取为字符串,但考虑到我自己最后摆脱了点,是否有任何内置的解析器函数可以使整数为“13”并[(Integer,Integer)]
列出[(1,2),(2,3),(4,5)]
?
是的,它被称为read
:
let i = read "13" :: Integer
let ts = read "[(1,2),(2,3),(4,5)]" :: [(Integer, Integer)]
您提供的示例文本文件包含尾随空格和句号,因此仅剪切最后一个字符是行不通的。让我们只取数字,使用:
import Data.Char (isDigit)
为什么没有数据类型来存储文件中的内容:
data MyStuff = MyStuff {firstNum :: Int,
secondNum:: Int,
intPairList :: [(Integer, Integer)]}
deriving (Show,Read)
现在我们需要读取文件,然后将其转换为单独的行:
getMyStuff :: FilePath -> IO MyStuff
getMyStuff filename = do
rawdata <- readFile filename
let [i1,i2,list] = lines rawdata
return $ MyStuff (read $ takeWhile isDigit i1) (read $ takeWhile isDigit i2) (read $ init list)
该read
函数适用于任何具有 Read 实例的数据类型,并自动生成正确类型的数据。
> getMyStuff "data.txt" >>= print
MyStuff {firstNum = 13, secondNum = 13, intPairList = [(1,2),(2,3),(4,5)]}
我倾向于为自己节省一些工作,直接写数据,所以
writeMyStuff :: FilePath -> MyStuff -> IO ()
writeMyStuff filename somedata = writeFile filename (show somedata)
readMyStuff :: FilePath -> IO MyStuff
readMyStuff filename = fmap read (readFile filename)
(fmap
只是将纯函数read
应用于 的输出readFile
。)
> writeMyStuff "test.txt" MyStuff {firstNum=12,secondNum=42, intPairList=[(1,2),(3,4)]}
> readMyStuff "test.txt" >>= print
MyStuff {firstNum = 12, secondNum = 42, intPairList = [(1,2),(3,4)]}
如果您让编译器为您整理所有内容,那么您不太可能产生很少的解析或打印错误,它的代码更少,更简单。
Haskell 的强类型要求你知道你得到了什么。所以让我们放弃所有的错误检查和优化,假设文件总是正确的格式,你可以这样做:
data Entry = Number Integer
| List [(Integer, Integer)]
parseYourFile :: FilePath -> IO [Entry]
parseYourFile p = do
content <- readFile p
return $ parseYourFormat content
parseYourFormat :: String -> [Entry]
parseYourFormat data = map parseEntry $ lines data
parseEntry :: String -> Entry
parseEntry line = if head line == '['
then List $ read core
else Number $ read core
where core = init line
或者,您可以使用众多组合器框架之一为其编写适当的解析器。