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" после печати строк, которые не дают результата.

Другие вопросы по тегам