当行结束序列为CRLF时,为什么此解析器总是失败?

时间:2015-09-03 06:28:04

标签: haskell attoparsec end-of-line

这个简单的解析器应该解析格式为

的消息
key: value\r\nkey: value\r\n\r\nkey: value\r\nkey: value\r\n\r\n

一个EOL充当字段分隔符,双EOL充当消息分隔符。当EOL分隔符为\nparseWith始终在\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

2 个答案:

答案 0 :(得分:3)

我假设您正在使用这些导入:

{-# 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")]]

答案 1 :(得分:1)

问题

您的代码不是自包含的,实际问题不明确。但是,我怀疑你的困境实际上是由解析键的方式引起的;特别是,\r\nk之类的东西是一个有效的密钥,根据你的解析器:

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

需要修复。

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

另一个切线问题:因为您使用many1组合器而非ByteString面向解析器(例如takeTill),您的值的类型为String而不是{{1 }}。这可能你想要什么,这里,因为它首先违背了使用ByteString的目的。见Performance considerations

解决方案

我建议进行以下重构:

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 - &gt; parsePair
  • pair - &gt; parseListPairs
  • msg - &gt; parseMsg

在GHCi中进行测试

msgs

好;在这种情况下,你确实想要失败。

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