Проблемы
Вашего код не самодостаточный и актуальная проблема остается неясной. Тем не менее, я подозреваю, что ваши проблемы на самом деле вызваны тем, как анализируются ключи; в частности, что-то вроде \r\nk
является действительным ключом, в соответствии с вашим анализатором:
λ> parseOnly parsePair "\r\nk: v\r\n"
Right ("\r\nk","v")
Это должно быть исправлено.
Кроме того, так как один оконечный отделяет (а не заканчивается) пар ключ-значением, оконечные не следует употреблять в конце вашего parsePair
синтаксического анализатора.
Другого тангенциальным вопроса: потому что вы используете many1
комбинатора вместо ByteString
-ориентированных парсеров (такие как takeTill
), ваши значения имеют тип String
вместо ByteString
. Это, вероятно, не, что вы хотите, здесь, потому что он в первую очередь поражает цель использования ByteString
.; см. Performance considerations.
Решение
Я предлагаю следующий рефакторинг:
{-# 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")]]
Откуда вы взяли 'endOfLine', или как вы его определили? Что означает 'B8'? Я могу сделать обоснованное предположение, но все же ... Вам нужно сделать код в своем вопросе самодостаточным, добавив свои операторы импорта и пользовательские определения. Вероятно, проблема заключается в том, что 'endOfLine' определяется для разбора' \ n', а не '\ r \ n'. Вам нужно будет определить свой собственный парсер 'endOfLine' для' \ r \ n'. – Jubobs
@Jubobs Наверное, нет. Скорее всего, это [endOfLine' attoparsec] (https://hackage.haskell.org/package/attoparsec-0.13.0.1/docs/Data-Attoparsec-Text.html#v:endOfLine). – duplode
@duplode Вы правы, но соответствующая ссылка, вероятно, [эта] (http://hackage.haskell.org/package/attoparsec-0.13.0.1/docs/Data-Attoparsec-ByteString-Char8.html#g : 13), как это было предложено под названием «B8». – Jubobs