4

这个简单的解析器预计会解析表单的消息

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

一个 EOL 充当字段分隔符,双 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")

这需要修复。

此外,由于一个 EOL分离(而不是终止)键值对,因此不应在parsePair解析器结束时使用 EOL。

另一个切线问题:因为您使用many1组合器而不是面向ByteString的解析器(例如takeTill),所以您的值具有 typeString而不是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)

为了与 's 保持一致,我已重命名您的解析器,attoparsec其中没有一个以“parse”作为前缀:

  • 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 回答