Haskell:将数据从文件转换为数据类型

时间:2015-03-17 19:46:54

标签: haskell

我无法将字符串列表从文本文件转换为我创建的名为Film的数据类型。我将在下面显示以下代码:

Inside the films.txt

"Casino Royale" 
"Daniel Craig", "Eva Green", "Judi Dench"
2006
"Garry", "Dave", "Zoe", "Kevin", "Emma"

"Cowboys & Aliens"
"Harrison Ford", "Daniel Craig", "Olivia Wilde"
2011
"Bill", "Jo", "Garry", "Kevin", "Olga", "Liz"

"Catch Me If You Can"
"Leonardo DiCaprio", "Tom Hanks"
2002
"Zoe", "Heidi", "Jo", "Emma", "Liz", "Sam", "Olga", "Kevin", "Tim"
}

我的Haskell代码:

type Title = String
type Actor = String
type Cast = [Actor]
type Year = Int
type Fan = String
type Fans = [Fan]

type Film = (Title, Cast, Year, Fans)

main ::  IO()
main = do 
      putStr "What is your name?: "
      name <- getLine
      firstdatabase <- readFile "films.txt"
      putStr firstdatabase
      let database = read firstdatabase :: [Film]
      mainLoop database name

关于我将要做什么的任何想法?

1 个答案:

答案 0 :(得分:4)

使用parsec非常容易实现:

import Text.Parsec
    ( Parsec, ParseError, parse        -- Types and parser
    , between, noneOf, sepBy, many1    -- Combinators
    , char, spaces, digit, newline     -- Simple parsers
    )

-- Parse a string to a string
stringLit :: Parsec String u String
stringLit = between (char '"') (char '"') $ many1 $ noneOf "\"\n"

-- Parse a string to a list of strings
listOfStrings :: Parsec String u [String]
listOfStrings = stringLit `sepBy` (char ',' >> spaces)

-- Parse a string to an int
intLit :: Parsec String u Int
intLit = fmap read $ many1 digit
-- Or `read <$> many1 digit` with Control.Applicative

film :: Parsec String u Film
film = do
    -- alternatively `title <- stringLit <* newline` with Control.Applicative
    title <- stringLit
    newline
    cast <- listOfStrings
    newline
    year <- intLit
    newline
    fans <- listOfStrings
    newline
    return (title, cast, year, fans)
-- Alternatively, you can define it all in one go (with Control.Applicative) as
-- film = (,,,)
--      <$> stringlit     <* newline
--      <*> listOfStrings <* newline
--      <*> intLit        <* newline
--      <*> listOfStrings <* newline
-- Which makes it look very much like your actual file

films :: Parsec String u [Film]
films = film `sepBy` newline

然后你可以用它作为

loadDB :: FilePath -> IO (Either ParseError [Film])
loadDB filename = do
    db <- readFile filename
    return $ parse films "films" db

main :: IO ()
main = do
    putStr "What is your name?"
    name <- getLine
    db' <- loadDB "films.txt"
    case db of
        Left err -> do
            putStrLn "Error loading film database:"
            print err
        Right db -> mainLoop db name