用Parsec跳过空白行

时间:2016-09-16 11:24:26

标签: haskell parsec

Parsec的新手,一个初学者的问题。如何解析一些行可能为空的行文件,只包含空格后跟换行符?我只是想跳过它们,而不是在解析的输出中使用它们。

import Text.ParserCombinators.Parsec

-- alias for parseTest
run :: Show a => Parser a -> String -> IO ()
run = parseTest

-- parse lines
p :: Parser [[String]]
p = lineP `endBy` newline <* eof
  where lineP = wordP `sepBy` (char ' ')
        wordP = many $ noneOf "\n"

用空行解析示例:

*Main> run p "z x c\n1 2 3\n    \na\n"
[["z x c"],["1 2 3"],["    "],["a"]]

我怀疑我这一切都错了。

2 个答案:

答案 0 :(得分:4)

您可以定义一个自定义解析器,而不是使用newline,它可以捕获行的结尾的您的概念,该解析器将解析至少一个newline,然后可选的许多空行(即空格后跟另一个换行符)。如果空格后面没有其他换行符(或者输入结束,我猜),你需要try运算符回溯:

代码:

-- parse lines
p :: Parser [[String]]
p = lineP `endBy` lineEnd <* eof
  where lineP = wordP `sepBy` (char ' ')
        wordP = many $ noneOf " \n"

lineEnd :: Parser ()
lineEnd = do
    newline
    many (try (many (oneOf " \t") >> newline))
    return ()

输出:

*Main> run p "z x c\n1 2 3\n    \na\n"
[["z","x","c"],["1","2","3"],["a"]]

答案 1 :(得分:4)

一种方法可能是将文件视为一系列空白或非空白的行。以下用表达式line <|> emptyLine表达了这个想法。以下使用Maybe数据类型来区分解析非空行的结果,使用catMaybes过滤掉Nothing s。

#!/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解析器存在一个小错误。另请注意,根据指定,这些解析器不处理前置或尾随空格(在非空行上)。我想象你的非最小代码更有弹性。