у меня работает:
Prelude Text.ParserCombinators.Parsec> let authorName = do { name <- many1 (noneOf ",:\r\n\8212\8213"); many (oneOf ",:-\8212\8213"); }
Prelude Text.ParserCombinators.Parsec> parse authorName "" "my Name,\8212::-:\8213,"
Right ",\8212::-:\8213,"
Как ты попробовал?
Выше использовался простой String
, который работает без проблем, потому что Char
- это полная кодовая точка без кода. Это не так хорошо с другими типами входного потока. Text
, вероятно, также будет хорошо работать для этого примера, я думаю, что тире там кодируются как единая единица кода. Однако для ByteString
все сложнее. Если вы используете обычный Data.ByteString.Char8
(строгий или ленивый, не имеет значения), Char
усекаются при упаковке, сохраняются только самые младшие 8 битов, поэтому '\ 8212' становится 20, а '\ 8213 'становится 21. Если входной поток построен таким же образом, который все еще работает, только все Char
, соответствующие 20 или 21 по модулю 256, будут сопоставлены с одной из черточек.
Однако, вполне вероятно, что входной поток кодируется в UTF-8, тогда тире кодируются как три байта каждый, "\ 226 \ 128 \ 148" соответственно. «\ 226 \ 128 \ 149», что не совпадает с тем, что вы получаете путем усечения. Попытка разобрать кодированный в utf-8 текст с помощью ByteString
и parsec
немного сложнее, единицы, из которых состоит результат анализа, представляют собой не отдельные байты, а последовательности байтов длиной 1-4 каждый.
Чтобы использовать noneOf
, вам нужен
instance Text.Parsec.Prim.Stream ByteString m Char
что делает правильно. Экземпляр, предоставленный в Text.Parsec.ByteString[.Lazy]
, не имеет, он использует интерфейс Data.ByteString[.Lazy].Char8
, поэтому точка дефиса станет единичным '\ 20', не соответствующим '\ 8212', или выдаст три Chars
, '\ 226', «\ 128» и «\ 148» в трех последовательных вызовах uncons
, ни один из которых не соответствует «\ 8212», в зависимости от того, как был закодирован вход.