解析额外行的Parsec csv解析器

时间:2012-07-23 19:11:31

标签: haskell parsec

我已经定义了以下Parsec解析器,用于将csv文件解析为字符串表,即[[String]]

--A csv parser is some rows seperated, and possibly ended, by a newline charater
csvParser = sepEndBy row (char '\n')
--A row is some cells seperated by a comma character
row = sepBy cell (char ',')
--A cell is either a quoted cell, or a normal cell
cell = qcell <|> ncell
--A normal cell is a series of charaters which are neither , or newline. It might also be an escape character
ncell = many (escChar <|> noneOf ",\n")
--A quoted cell is a " followd by some characters which either are escape charaters or normal characters except for "
qcell = do
    char '"'
    res <- many (escChar <|> noneOf "\"")
    char '"'
    return res
--An escape character is anything followed by a \. The \ will be discarded.
escChar = char '\\' >> anyChar

我真的不知道这些评论是否太过烦人,如果他们有所帮助的话。作为Parsec noob他们会帮助我,所以我想我会添加它们。

它工作得很好,但有一个问题。它在表中创建了一个额外的空行。因此,如果我有一个包含10行的csv文件(即只有10行。最后没有空行 *),[[String]]结构的长度为11,最后一个String的列表将包含1个元素。空String(至少这是使用show打印时的显示方式。)

我的主要问题是:为什么会出现这个额外的行,我该怎么做才能阻止它?

我注意到的另一件事是,如果csv文件中的数据后面有空行,这些行将最终成为表中只包含空String的行。我认为使用sepEndBy代替sepBy会使多余的空行被忽略。情况不是这样吗?

*在十六进制编辑器中查看文本文件之后,它似乎确实以换行符结尾,即使vim没有显示它...

1 个答案:

答案 0 :(得分:2)

如果您希望每行至少有一个单元格,则可以使用sepBy1代替sepBy。这也应该阻止空行被解析为一行。 sepBysepBy1之间的差异与manymany1之间的差异相同:1版本仅解析至少一个元素的序列。所以row变成了这个:

row = sepBy1 cell (char ',')

此外,通常的方式是在中缀sepBy1中使用cell `sepBy1` char ','。这更自然地读取:你有一个“逗号分隔的单元格”,而不是“用逗号分隔单元格”。

编辑:如果您不想接受空单元格,则必须使用ncell指定many1至少包含一个字符:

ncell = many1 (escChar <|> noneOf ",\n")