我已经定义了以下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没有显示它...
答案 0 :(得分:2)
如果您希望每行至少有一个单元格,则可以使用sepBy1
代替sepBy
。这也应该阻止空行被解析为一行。 sepBy
和sepBy1
之间的差异与many
和many1
之间的差异相同:1
版本仅解析至少一个元素的序列。所以row
变成了这个:
row = sepBy1 cell (char ',')
此外,通常的方式是在中缀sepBy1
中使用cell `sepBy1` char ','
。这更自然地读取:你有一个“逗号分隔的单元格”,而不是“用逗号分隔单元格”。
编辑:如果您不想接受空单元格,则必须使用ncell
指定many1
至少包含一个字符:
ncell = many1 (escChar <|> noneOf ",\n")