2016-09-16 12 views
2

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

なぜ 'filter(any(isSpaceではない))だけでなく、ライン '? – melpomene

+0

質問を簡略化するために私が行っていることの詳細を抽象化しました。私は、単語だけでなく、さまざまな複雑なソートのキーと値に構成ファイルを解析しています。パーサーは、文法の一環として空白行を捨てることができなければならないと思います。ファイルレベルのレキシカルな読み方では、この行為をしてはいけません。 – andro

+0

'wordP'はスペースを解析するので、' \ 'sepBy \'(char '') 'は実際に何もしません。 –

答えて

3

代わりnewlineを使用するには、少なくとも1つの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"]] 
3

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パーサーには小さなバグがあります。また、指定されているように、これらのパーサーは、空白でない行の前後のスペースに対処しないことにも注意してください。私はあなたの非最小コードがより弾力性があることをイメージしています。

関連する問題