一种方法可能是将文件视为一系列空白或非空白的行。下面用表达式line <|> emptyLine
表达这个想法。以下内容使用Maybe
数据类型来区分解析非空行的结果,使用catMaybes
最后过滤掉Nothing
。
#!/usr/bin/env stack
{- stack
--resolver lts-7.0
--install-ghc
runghc
--package parsec
-}
import Prelude hiding (lines)
import Data.Maybe (catMaybes)
import Text.ParserCombinators.Parsec
-- parse lines
p :: Parser [[String]]
p = catMaybes <$> lines
where lines = (line <|> emptyLine) `endBy` newline <* eof
line = Just <$> word `sepBy1` spaces1
emptyLine = spaces1 >> pure Nothing
word = many1 $ noneOf ['\n', ' ']
spaces1 = skipMany1 (char ' ')
main = parseTest p "z x c\n1 2 3\n \na\n"
输出是:
[["z","x","c"],["1","2","3"],["a"]]
另一种方法可能是使用Prelude
功能与Data.Char.isSpace
一起收集非空行,然后再开始:
#!/usr/bin/env stack
{- stack
--resolver lts-7.0
--install-ghc
runghc
--package parsec
-}
import Data.Char
import Text.ParserCombinators.Parsec
p :: Parser [[String]]
p = line `endBy` newline <* eof where
line = word `sepBy1` spaces1
word = many1 $ noneOf ['\n', ' ']
spaces1 = skipMany1 (char ' ')
main = parseTest p (unlines nonBlankLines)
where input = "z x c\n1 2 3\n \na\n"
nonBlankLines = filter (not . all isSpace) $ lines input
输出是:
[["z","x","c"],["1","2","3"],["a"]]
这非常简单,并且具有额外的好处,即使用lines
将不需要每行末尾的newline
(这有助于便携性)。
请注意,您的wordP
解析器存在一个小错误。还要注意,如指定的那样,这些解析器不能处理前面或后面的空格(在非空行上)。我正在想象你的非最小代码更具弹性。
为什么不只是'筛选(任何(不是。isSpace))。 lines'? – melpomene
为了简化问题,我抽取了我正在做的事情的细节。我正在解析配置文件,不仅仅是文字,而是解析各种复杂类型的键和值。在我看来,解析器应该能够放弃作为其语法一部分的空白行,并且不应该在文件级别的词法读取时执行此操作。 – andro
你的'wordP'解析空格,所以'\'sepBy \'(char'')'什么也不做。 –