Haskell:编写文本文件并将其解析回原始格式

时间:2009-05-06 14:57:29

标签: file parsing haskell

我有一个格式[(String,String)]的元组列表,我需要一个函数将列表的内容写入文本文件,然后另一个函数将该文本文件作为相同的元组列表读取。以下是我为保存功能所做的事情:

save :: Table -> IO()
save [] = writeFile "database.txt" ""
save zs = do { writeFile "database.txt" "" ; sequence_ [appendFile "database.txt" ("("++a++","++b++")\n") | (a,b) <- zs] }

这是一个很好的文本文件格式吗?那么我怎样才能读取该文本文件并将其转换回元组列表?

3 个答案:

答案 0 :(得分:12)

Prelude中定义,

type ShowS = String -> String
class Show a where
    showsPrec :: Int -> a -> ShowS
    show :: a -> String
    showList :: [a] -> ShowS

type ReadS a = String -> [(a, String)]
class Read a where
    readsPrec :: Int -> ReadS a
    readList :: ReadS [a]
read :: (Read a) => String -> a

简而言之,这些是Haskell中的标准“序列化”方法。 show :: (Show a) => a -> String可以将Show实例的任何内容转换为字符串,read :: (Read a) => String -> a可以将字符串转换为Read的实例(或抛出异常)

标准库中的大多数内置类型和数据结构都定义了ShowRead个实例;如果您正在撰写部件,则您的类型也会定义ShowRead个实例。

type Table = [(String, String)]

load :: (Read a) => FilePath -> IO a
load f = do s <- readFile f
            return (read s)

save :: (Show a) => a -> FilePath -> IO ()
save x f = writeFile f (show x)

如果Table是数据类型,则必须要求实例,但您可以请求编译器自动为您导出它们。

data Table = Table [(String, String)]
    deriving (Read, Show)

有时这是不可能的,你必须定义自己的实例。

instance Show Table where
    showsPrec p x = ...
instance Read Table where
    readsPrec p x = ...

但这不应该是常见的。

答案 1 :(得分:5)

show / read方法可以正常使用,我也使用它,但仅适用于小值。更大,更复杂的值read将非常慢。

这个人为的例子证明了read的糟糕表现:

data RevList a = (RevList a) :< a | Nil
  deriving (Show, Read)

ghci> read "(((((((((((((((Nil)))))))))))))))" :: RevList Int

此外,read将无法读取一些有效的Haskell表达式,尤其是那些使用中缀构造函数的表达式(如我的示例中的:<)。原因是read没有意识到运营商的固定性。这也是show $ Nil :< 1 :< 2 :< 3会产生许多看似多余的括号的原因。

如果您想要更大值的序列化,我建议使用其他库,如Data.Binary。这比简单的show稍微复杂一些,主要是因为缺少deriving Binary。但是,有各种通用编程解决方案可以为您提供deriving - 就像代理人一样。

结论:我会说,使用show / read解决方案直到达到极限(可能一旦开始构建实际应用程序),然后开始查看像Data.Binary那样可扩展(但也更复杂)的东西。


旁注:对那些对解析器和更高级的Haskell感兴趣的人;我提供的示例来自论文:Haskel Do You Read Me?,另一种是 fast read - 就像函数一样。

答案 2 :(得分:3)

使用当前函数时,如果列表中的字符串包含“,”或“)”,则会出现问题,因为当您尝试再次读取数据时,无法找到字符串的结束位置。只要它们出现在字符串中,您就需要以某种方式转义这些字符。

使用showread将数据转换为字符串然后自行完成更容易:

save :: Table -> IO ()
save zs = writeFile "database.txt" (show zs)

show转义特殊字符,并确保数据的格式可由read解析。要加载数据,您可以将文件读入字符串并将其传递给read,以将其转换为所需的数据结构。