1つの方法は、ファイルを空白または空白でない一連の行として考えることです。以下は、この考え方を式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"]]
あなたが始める前に、別のアプローチは、非空白行を収集するためにData.Char.isSpace
とともにPrelude
機能を使用することがあります
#!/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
パーサーには小さなバグがあります。また、指定されているように、これらのパーサーは、空白でない行の前後のスペースに対処しないことにも注意してください。私はあなたの非最小コードがより弾力性があることをイメージしています。
なぜ 'filter(any(isSpaceではない))だけでなく、ライン '? – melpomene
質問を簡略化するために私が行っていることの詳細を抽象化しました。私は、単語だけでなく、さまざまな複雑なソートのキーと値に構成ファイルを解析しています。パーサーは、文法の一環として空白行を捨てることができなければならないと思います。ファイルレベルのレキシカルな読み方では、この行為をしてはいけません。 – andro
'wordP'はスペースを解析するので、' \ 'sepBy \'(char '') 'は実際に何もしません。 –