haskell конвертирует юникодную последовательность в utf 8
Я работаю над http-клиентом в haskell (это мой первый проект "не exersize").
Существует API, который возвращает JSON со всем текстом, используя Unicode, что-то вроде
\u041e\u043d\u0430 \u043f\u0440\u0438\u0432\u0435\u0434\u0435\u0442 \u0432\u0430\u0441 \u0432 \u0434\u043b\u0438\u043d\u043d\u044b\u0439 \u0441\u043f\u0438\u0441\u043e\u043a
Я хочу декодировать этот JSON в UTF-8, чтобы напечатать некоторые данные из сообщения JSON.
Я искал существующие библиотеки, но ничего не нашел для этой цели.
Поэтому я написал функцию для преобразования данных (я использую ленивые строки байтов, потому что я получил данные с этим типом из wreq lib)
ununicode :: BL.ByteString -> BL.ByteString
ununicode s = replace s where
replace :: BL.ByteString -> BL.ByteString
replace str = case (Map.lookup (BL.take 6 str) table) of
(Just x) -> BL.append x (replace $ BL.drop 6 str)
(Nothing) -> BL.cons (BL.head str) (replace $ BL.tail str)
table = Map.fromList $ zip letters rus
rus = ["Ё", "ё", "А", "Б", "В", "Г", "Д", "Е", "Ж", "З", "И", "Й", "К", "Л", "М",
"Н", "О", "П", "Р", "С", "Т", "У", "Ф", "Х", "Ц", "Ч", "Ш", "Щ", "Ъ", "Ы",
"Ь", "Э", "Ю", "Я", "а", "б", "в", "г", "д", "е", "ж", "з", "и", "й", "к",
"л", "м", "н", "о", "п", "р", "с", "т", "у", "ф", "х", "ц", "ч", "ш", "щ",
"ъ", "ы", "ь", "э", "ю", "я"]
letters = ["\\u0401", "\\u0451", "\\u0410", "\\u0411", "\\u0412", "\\u0413",
"\\u0414", "\\u0415", "\\u0416", "\\u0417", "\\u0418", "\\u0419",
"\\u041a", "\\u041b", "\\u041c", "\\u041d", "\\u041e", "\\u041f",
"\\u0420", "\\u0421", "\\u0422", "\\u0423", "\\u0424", "\\u0425",
"\\u0426", "\\u0427", "\\u0428", "\\u0429", "\\u042a", "\\u042b",
"\\u042c", "\\u042d", "\\u042e", "\\u042f", "\\u0430", "\\u0431",
"\\u0432", "\\u0433", "\\u0434", "\\u0435", "\\u0436", "\\u0437",
"\\u0438", "\\u0439", "\\u043a", "\\u043b", "\\u043c", "\\u043d",
"\\u043e", "\\u043f", "\\u0440", "\\u0441", "\\u0442", "\\u0443",
"\\u0444", "\\u0445", "\\u0446", "\\u0447", "\\u0448", "\\u0449",
"\\u044a", "\\u044b", "\\u044c", "\\u044d", "\\u044e", "\\u044f"]
Но это не работает, как я ожидал. Он заменяет текст, но вместо кириллических букв я получил что-то вроде 345?C1;8:C5< 8=B5@2LN A @4=52=8:>2F0<8 8=B5@5A=KE?@>D5AA89 8 E>118
Вторая проблема - я не могу отладить свою функцию. Когда я пытаюсь просто вызвать его с пользовательской строкой, я получил ошибку Data.ByteString.Lazy.head: empty ByteString
Я понятия не имел, почему он пуст.
Это нормально работает во время нормального выполнения программы:
umailGet env params = do
r <- apiGet env (("method", "umail.get"):params)
x <- return $ case r of
(Right a) -> a
(Left a) -> ""
return $ ununicode $ x
и чем в основном
r2 <- umailGet client []
print $ r2
И последняя проблема заключается в том, что все API могут возвращать любой символ Unicode, поэтому это решение является плохим по дизайну.
Конечно, реализация функции кажется плохой, поэтому после решения основной проблемы я собираюсь переписать ее с помощью Foldr.
ОБНОВЛЕНО: Кажется, я описал проблему недостаточно ясно.
Поэтому я отправляю запрос через wreq lib и получаю ответ json. Например
{"result":"12","error":"\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440 \u0441\u0435\u0441\u0441\u0438\u0438"}
Это не результат представления результата в haskell, это реальные символы ascii. Я получил тот же текст, используя curl или firefox. 190 байтов /190 символов ASCII.
Используя этот сайт, например, http://unicode.online-toolz.com/tools/text-unicode-entities-convertor.php я могу преобразовать его в кириллический текст {"result":"12","error":"Неверный идентификатор сессии"}
И мне нужно реализовать что-то вроде этого сервиса, используя haskell (или найти пакет, в котором он уже был реализован), где ответ вроде этого имеет тип Lazy Bytestring.
Я также попытался изменить типы, чтобы использовать Text вместо ByteString (ленивый и строгий), изменил первую строку на ununicode s = encodeUtf8 $ replace $ L.toStrict $ LE.decodeUtf8 s
И с этой новой реализацией я получаю ошибку при выполнении моей программы Data.Text.Internal.Fusion.Common.head: Empty stream
, Похоже, у меня есть ошибка в моей заменяющей функции, возможно, если я исправлю это, это также решит основную проблему.
1 ответ
Я не уверен, попадаете ли вы в ловушку "print unicode" (см. Здесь) - для en/decoding уже существует хакерство: Data.Text.Encoding decodeUtf8 :: ByteString -> Text
а также encodeUtf8 :: Text -> ByteString
должен сделать задачу.
Редактировать:
Я какое-то время играл с текстом / байтовой строкой, чтобы воспроизвести ваши символы "\u1234" - ну, я не смог
{-# LANGUAGE OverloadedStrings #-}
module Main where
import Data.Text (Text)
import qualified Data.Text.Encoding as E
import qualified Data.Text.IO as T
import Data.ByteString (ByteString)
import qualified Data.ByteString.Char8 as B
inputB :: ByteString
inputB = "ДЕЖЗИЙКЛМНОПРСТУФ"
inputT :: Text
inputT = "ДЕЖЗИЙКЛМНОПРСТУФ"
main :: IO ()
main = do putStr "T.putStrLn inputT: " ; T.putStrLn inputT
putStr "B.putStrLn inputB: " ; B.putStrLn inputB
putStr "print inputB: " ; print inputB
putStr "print inputT: " ; print inputT
putStr "B.putStrLn $ E.encodeUtf8 inputT: " ; B.putStrLn $ E.encodeUtf8 inputT
putStr "T.putStrLn $ E.decodeUtf8 inputB: " ; T.putStrLn $ E.decodeUtf8 inputB
putStr "print $ E.decodeUtf8 inputB: " ; print $ E.decodeUtf8 inputB
putStr "print $ E.encodeUtf8 inputT: " ; print $ E.encodeUtf8 inputT
вот результат этого:
T.putStrLn inputT: ДЕЖЗИЙКЛМНОПРСТУФ
B.putStrLn inputB:
rint inputB: "\DC4\NAK\SYN\ETB\CAN\EM\SUB\ESC\FS\GS\RS\US !\"#$"
print inputT: "\1044\1045\1046\1047\1048\1049\1050\1051\1052\1053\1054\1055\1056\1057\1058\1059\1060"
B.putStrLn $ E.encodeUtf8 inputT: ДЕЖЗИЙКЛМНОПРСТУФ
T.putStrLn $ E.decodeUtf8 inputB:
rint $ E.decodeUtf8 inputB: "\DC4\NAK\SYN\ETB\CAN\EM\SUB\ESC\FS\GS\RS\US !\"#$"
print $ E.encodeUtf8 inputT: "\208\148\208\149\208\150\208\151\208\152\208\153\208\154\208\155\208\156\208\157\208\158\208\159\208\160\208\161\208\162\208\163\208\164"
честно говоря, я не знаю, почему я получаю строки "rint" после печати строк, которые не дают результата.