4

この単純なパーサーは、次の形式のメッセージを解析することが期待されています

key: value\r\nkey: value\r\n\r\nkey: value\r\nkey: value\r\n\r\n

1 つの EOL はフィールド セパレータとして機能し、2 つの EOL はメッセージ セパレータとして機能します。EOLセパレーターが の場合は完全に正常に動作し\nますが、 のparseWith場合は常に失敗を返します\r\n

parsePair = do
  key <- B8.takeTill (==':')
  _ <- B8.char ':'
  _ <- B8.char ' '
  value <- B8.manyTill B8.anyChar endOfLine
  return (key, value)

parseListPairs = sepBy parsePair endOfLine <* endOfLine

parseMsg = sepBy parseListPairs endOfLine <* endOfLine
4

2 に答える 2

4

これらのインポートを使用していると仮定しています:

{-# LANGUAGE OverloadedStrings #-}
import qualified Data.Attoparsec.ByteString.Char8 as B8
import Data.Attoparsec.ByteString.Char8

問題はendOfLine、行末を消費することです。そのため、おそらく次のようなものが本当に必要です。

parseListPairs = B8.many1 parsePair <* endOfInput

たとえば、これは機能します:

ghci> parseOnly parseListPairs "k: v\r\nk2: v2\r\n"
Right [("k","v"),("k2","v2")]

アップデート:

複数のメッセージを解析するには、次を使用できます。

parseListPairs = B8.manyTill parsePair endOfLine
parseMsgs = B8.manyTill parseListPairs endOfInput

ghci> test3 = parseOnly parseMsgs "k1: v1\r\nk2: v2\r\n\r\nk3: v3\r\nk4: v4\r\n\r\n"
Right [[("k1","v1"),("k2","v2")],[("k3","v3"),("k4","v4")]]
于 2015-09-03T06:53:56.187 に答える
2

問題

あなたのコードは自己完結型ではなく、実際の問題は不明です。ただし、実際にはキーの解析方法が原因であると思われます。特に、\r\nkパーサーによると、次のようなものが有効なキーです。

λ> parseOnly parsePair "\r\nk: v\r\n"
Right ("\r\nk","v")

それを修正する必要があります。

さらに、1 つの EOLが (終了するのではなく) キーと値のペアを分離するため、EOL はパーサーの最後で消費されるべきではありません。parsePair

別の接線の問題:many1コンビByteStringネーター指向のパーサー ( などtakeTill) を使用するため、値の型Stringは ではなく になりByteStringます。それは、そもそも使用の目的に反するため、ここではおそらくあなたが望むものではありません。; パフォーマンスに関する考慮事項ByteStringを参照してください。

解決

次のリファクタリングをお勧めします。

{-# LANGUAGE OverloadedStrings #-}

import Data.ByteString ( ByteString )

import Data.Attoparsec.ByteString.Char8 ( Parser
                                        , count
                                        , endOfLine
                                        , parseOnly
                                        , sepBy
                                        , string
                                        , takeTill
                                        )

-- convenient type synonyms
type KVPair = (ByteString, ByteString)
type Msg = [KVPair]

pair :: Parser KVPair
pair = do
    k <- key
    _ <- string ": "
    v <- value
    return (k, v)
  where
    key     = takeTill (\c -> c == ':' || isEOL c)
    value   = takeTill isEOL
    isEOL c = c == '\n' || c == '\r'

-- one EOL separates key-value pairs
msg :: Parser Msg
msg = sepBy pair endOfLine

-- two EOLs separate messages
msgs :: Parser [Msg]
msgs = sepBy msg (count 2 endOfLine)

のパーサーとの一貫性を保つために、あなたのパーサーの名前を変更しattoparsecました。

  • parsePair-->pair
  • parseListPairs-->msg
  • parseMsg-->msgs

GHCi でのテスト

λ> parseOnly keyValuePair  "\r\nk: v"
Left "string"

良い; この場合、失敗が必要です。

λ> parseOnly keyValuePair  "k: v"
Right ("k","v")

λ> parseOnly msg "k: v\r\nk2: v2\r\n"
Right [("k","v"),("k2","v2")]

λ> parseOnly msgs  "k1: v1\r\nk2: v2\r\n\r\nk3: v3\r\nk4: v4"
Right [[("k1","v1"),("k2","v2")],[("k3","v3"),("k4","v4")]]

λ> parseOnly msgs "k: v"
Right [[("k","v")]]
于 2015-09-03T08:40:22.777 に答える