一種方法可能是將文件視爲一系列空白或非空白的行。下面用表達式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'')'什麼也不做。 –