使用Parsec解析几种不同的字段

时间:2014-10-23 15:28:50

标签: parsing haskell parsec

我有一个小的parsec解析器,可以将制表符分隔值(TSV)解析为字符串。我想切换到检查源文件中的数字和布尔值(列为" Y"或" N")。

这里是旧的TSV版本(返回[[String]]

tsvFile = endBy line newline
line = sepBy cell tab
cell = many (noneOf "\t\n")

我想更改它以支持这些类型:

data Cell = CellString String
          | CellNumber Int
          | CellBool Bool          
          deriving (Show)

以下是我为数字和布尔定义的函数。这些不正确吗?

cellBool = do
    b <- oneOf "YN"
    return $ CellBool (b == 'Y')

cellNumber = do
    d <- many digit
    return $ CellNumber (read d)

cellString = do
    s <- many (noneOf "\t\n")
    return $ CellString s

这就是我认为我需要做的才能让它发挥作用:

cell = cellBool <|> cellNumber <|> cellString

但它不起作用。在cellString返回Right []之前运行cellNumber。如果我将cellString放在列表中的第一位,它会将整个文件解析为字符串。

我确定我遗漏了一些基本的东西。比如,我认为只有cellString方法正在处理标签分隔符,但我对parsec和混淆并不熟悉。感谢您的帮助!

2 个答案:

答案 0 :(得分:2)

我只需更改cellNumber

的定义就能让它正常工作
cellNumber = do
    d <- many1 digit
    return $ CellNumber (read d)

问题在于cellNumber因使用many而读取空字符串。使用many1表示解析器失败,允许cellString执行。

但是,此时您的解析器会在"123a\n"之类的输入上失败,因此您需要找出回溯以使其正常工作。


使用定义

cellNumber = do
    d <- many1 digit
    lookAhead $ oneOf "\t\n"
    return $ CellNumber (read d)

可能并不理想。相反,我会考虑像

这样的东西
cellNumber = do
    d <- many1 digit
    notFollowedBy cellString
    return $ CellNumber (read d)

然后将您的cell功能更改为

cell = try cellBool <|> try cellNumber <|> cellString

答案 1 :(得分:1)

首先,您应该按照

的顺序运行解析器
parse (tsvFile <* eof) nm s

否则,Parsec将找到匹配的文件的最长前缀,并在第一次解析错误后丢弃每一行。

其次,您的某些文本字段是否可能以字符&#34; Y&#34;开头。或&#34; N&#34;还是用数字? Parsec只会对这些字段尝试cellBoolcellNumber,并且当他们不匹配时,整个解析都会失败。您可能希望在try中包含这两个备选方案,以告诉Parsec如果在该字段的第一个字符后失败一段时间后继续下一个匹配:

cell = try cellBool <|> try cellNumber <|> cellString
    -- Don't need try on cellString as it's the last alternative
相关问题