Как GHC/Haskell решает, какой кодирующий символ он будет декодировать/кодировать с/на?
Кажется, что GHC по крайней мере противоречива в кодировке символов, которую он решает декодировать.
Рассмотрим файл omatase-shimashita.txt
со следующим содержимым, закодированным в UTF-8: お 待 た せ し ま し た
readFile
, кажется, читает это правильно...
Prelude> content <- readFile "/home/chris/Desktop/omatase-shimashita.txt"
Prelude> length content
8
Prelude> putStrLn content
お待たせしました
Однако, если я пишу простой сервер "эхо", он не декодируется с дефолтом UTF-8. Рассмотрим следующий код, который обрабатывает входящий клиент:
handleClient handle = do
line <- hGetLine handle
putStrLn $ "Read following line: " ++ toString line
handleClient handle
И соответствующий код клиента, явно отправляющий UTF-8:
Data.ByteString.hPutStrLn handle $ Codec.Binary.UTF8.Generic.fromString "お待たせしました"
Это не противоречивое поведение? Есть ли способ этого безумия? Я планирую переписать свои приложения, чтобы явно использовать объекты ByteString
и явно кодировать и декодировать с помощью Codec.Binary.UTF8
, но было бы хорошо знать, что здесь происходит в любом случае...: o/
UPDATE: я запускаю Ubuntu Linux, версия 10.10, с локалью en_US.UTF-8...
$ cat /etc/default/locale
LANG="en_US.UTF-8"
$ echo $LANG
en_US.UTF-8
Ответы
Ответ 1
Какую версию GHC вы используете? Более старые версии особенно не очень хорошо вносили операции ввода-вывода в Юникоде.
В этом разделе документации GHC описано, как изменять кодировки ввода/вывода:
http://haskell.org/ghc/docs/6.12.2/html/libraries/base-4.2.0.1/System-IO.html#23
Кроме того, в документации указано следующее:
Ручка текстового режима имеет связанный TextEncoding, который используется для декодирования байтов в символы Unicode, когда чтение и кодирование символов Unicode в байтах при записи.
По умолчанию TextEncoding - это то же самое как кодировка по умолчанию на вашем системы, которая также доступна как localeEncoding. (Примечание GHC: в Windows, мы в настоящее время не поддерживаем двухбайтовые кодировки; если страница консоли не поддерживается, то localeEncoding будет latin1.)
Ошибки кодирования и декодирования всегда обнаруживается и сообщается, кроме во время ленивого ввода-вывода (hGetContents, getContents и readFile), где ошибка декодирования просто приводит к завершение символьного потока, как и при других ошибках ввода/вывода.
Возможно, это имеет какое-то отношение к вашей проблеме? Если GHC по умолчанию не использовал что-то, кроме utf-8, или ваш дескриптор вручную настроен на использование другой кодировки, что может объяснить проблему. Если вы просто пытаетесь эхо-текст на консоли, то, вероятно, происходит какая-то консольная кодовая страница. Я знаю, что у меня были аналогичные проблемы в прошлом с другими языками, такими как Python, и печать юникода в консоли Windows.
Попробуйте запустить hSetEncoding handle utf8
и проверьте, исправляет ли он вашу проблему.
Ответ 2
В первом примере используется стандартная библиотека ввода-вывода, System.IO
. Операции в этой библиотеке используют системную кодировку по умолчанию (также известную как localeEncoding
), если вы не указали иначе. Предположительно, ваша система настроена на использование UTF-8, так что это кодировка, используемая putStrLn
, hGetContents
и т.д.
В следующем примере используется Data.ByteString
. Поскольку эта библиотека обрабатывает только последовательности байтов, она не кодирует и не декодирует. Итак, Data.ByteString.hGetLine
преобразует байты в файл непосредственно в ByteString
.
Лучший способ сделать текстовый ввод-вывод в общем случае - использовать пакет text.