Как 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/

ОБНОВЛЕНИЕ: я работаю на Ubuntu Linux версии 10.10 с локалью en_US.UTF-8...

$ cat /etc/default/locale 
LANG="en_US.UTF-8"
$ echo $LANG 
en_US.UTF-8

person Chris W.    schedule 13.03.2011    source источник
comment
В конце концов я использовал Codec.Binary.UTF8 для кодирования/декодирования и Data.ByteString для отправки/получения необработанных байтов. Предположительно, решение chrisdb должно работать; для меня это работало, когда я тестировал его с моим игрушечным сервером/клиентом, но не работал, когда я пробовал его в своем реальном приложении (вероятно, из-за того, что я упустил какую-то деталь, но я потерял терпение и пошел с ByteString подход).   -  person Chris W.    schedule 14.03.2011


Ответы (2)


Какую версию GHC вы используете? Особенно старые версии не очень хорошо справлялись с вводом-выводом Unicode.

В этом разделе документации GHC описывается, как изменить кодировку ввода/вывода:

http://haskell.org/ghc/docs/6.12.2/html/libraries/base-4.2.0.1/System-IO.html#23

Кроме того, в документации говорится следующее:

У дескриптора текстового режима есть связанный TextEncoding, который используется для декодирования байтов в символы Юникода при чтении и кодирования символов Юникода в байты при записи.

TextEncoding по умолчанию совпадает с кодировкой по умолчанию в вашей системе, которая также доступна как localeEncoding. (Примечание GHC: в настоящее время в Windows мы не поддерживаем двухбайтовые кодировки; если кодовая страница консоли не поддерживается, то localeEncoding будет latin1.)

Ошибки кодирования и декодирования обнаруживаются и сообщаются всегда, за исключением ленивого ввода-вывода (hGetContents, getContents и readFile), где ошибка декодирования просто приводит к завершению символьного потока, как и в случае других ошибок ввода-вывода.

Может быть, это как-то связано с вашей проблемой? Если GHC где-то по умолчанию использует что-то отличное от utf-8, или ваш дескриптор вручную настроен на использование другой кодировки, это может объяснить проблему. Если вы просто пытаетесь отобразить текст на консоли, то, вероятно, происходит какая-то забавная консольная кодовая страница. Я знаю, что в прошлом у меня были подобные проблемы с другими языками, такими как Python, и печатью unicode в консоли Windows.

Попробуйте запустить hSetEncoding handle utf8 и посмотрите, решит ли это вашу проблему.

person chrisdb    schedule 13.03.2011
comment
Резюме: если вы повторяете вывод на консоль / стандартный вывод, это, вероятно, связано с кодировкой вашей системы по умолчанию или GHC по умолчанию использует latin1 в Windows. - person chrisdb; 13.03.2011
comment
К вашему сведению, это не проблема Windows, и я не думаю, что это проблема конкретного терминала. Я использую Ubuntu с локалью en_US.UTF-8. cat /etc/default/locale дает LANG="en_US.UTF-8", а echo $LANG дает en_US.UTF-8. - person Chris W.; 14.03.2011

В вашем первом примере используется стандартная библиотека ввода-вывода System.IO. Операции в этой библиотеке используют системную кодировку по умолчанию (также известную как localeEncoding), если не указано иное. Предположительно, ваша система настроена на использование UTF-8, так что это кодировка, используемая putStrLn, hGetContents и так далее.

В вашем втором примере используется Data.ByteString. Поскольку эта библиотека работает только с последовательностями байтов, она не выполняет ни кодирование, ни декодирование. Таким образом, Data.ByteString.hGetLine преобразует байты в файле напрямую в ByteString.

Наилучший способ выполнения текстового ввода-вывода — использовать пакет text.

person Simon Marlow    schedule 14.03.2011
comment
Да, я понимаю разницу между стандартным символом String и ByteString... Я просто указал, что я определенно отправлял текст в кодировке UTF-8 по сети, но по какой-то причине получатель (используя System.IO.hGetLine) не декодировал с использованием UTF-8. - person Chris W.; 14.03.2011