utf8: глюк или фича?

82 views
Skip to first unread message

nsk.coder

unread,
Aug 6, 2009, 4:24:28 AM8/6/09
to SPb Haskell User Group
Народ!
Кто может обяснить состояние дел с utf8 и локальными кодировками в GHC
6.10.3 (сборка под Windows)?

И компилятор и интрепретатор требуют, чтобы сторки в коде программы
были в utf8.
Например, код вида (в кодировке win-1251 или dos-866):

main = putStrLn "Привет мир!"

(ну или любой другой код, где есть кириллические строковые литералы)
вызывает ошибку:

Hello.hs:1:17:
lexical error in string/character literal (UTF-8 decoding error)

Понятное дело, что если код записать в utf8, то программа работает,
однако возникает проблема с отображением в виндовой консоли (где стоит
по умолчанию кодировка 866), ну или с родными для Windows текстовыми
файлами с кодировкой 1251.

Мне кажется, что в более ранних сборках GHC такого не было.

Что делать? Есть ли нормальный способ работь с локальными (не utf8)
кодировками: задавать их в программе в строковых литералах,
конвертировать и т.п.?

Проблема обостряется тем, что ни одного указанного в документации
пакета для работы с utf8 не установлено в GHC 6.10.3:

Codec.Binary.UTF8.Generic
Codec.Binary.UTF8.String
Data.ByteString.Lazy.UTF8
Data.ByteString.UTF8
System.IO.UTF8

(все имеет отношение к utf8-string-0.3.4)

Yakov ZAYTSEV

unread,
Aug 6, 2009, 4:27:13 AM8/6/09
to spb...@googlegroups.com
http://lukeplant.me.uk/blog.php?id=1107301701

planet.haskell.org читаешь? ;-)

2009/8/6 nsk.coder <nsk....@gmail.com>:

--
Best wishes,
Y

Iliya Kuznetsov

unread,
Aug 6, 2009, 9:05:44 AM8/6/09
to spb...@googlegroups.com

6 августа 2009 г. 12:24 пользователь nsk.coder <nsk....@gmail.com> написал:


И компилятор и интрепретатор требуют, чтобы сторки в коде программы
были в utf8.
Например, код вида (в кодировке win-1251 или dos-866):

Вообще это проблема исключительно и только виндовой консоли, которая требует такую перекодировку. По мне это на совести каждой отдельной программы с нерусской локалью.

В любой системе где локаль запуска юникодная, таких проблем нет.

--
Iliya Kuznetsov
+7-916-878-75-70

Miguel

unread,
Aug 7, 2009, 3:18:08 AM8/7/09
to spb...@googlegroups.com
Чего-то я не понял. Только что, под виндой, сделал файл с русским
текстом в UTF-8. Команда type в консоли отображает его совершенно
адекватно.

У меня (на работе) Windows XP.

2009/8/6 nsk.coder <nsk....@gmail.com>:

Iliya Kuznetsov

unread,
Aug 7, 2009, 4:20:23 AM8/7/09
to spb...@googlegroups.com
а type точно команда command.com а не внешняя программа (скажем из Cygwin)?
У меня в windows 2003 type по юникодному (utf-8) файлу выдаёт зюки.

7 августа 2009 г. 11:18 пользователь Miguel <mig...@gmail.com> написал:



--
Iliya Kuznetsov
+7-916-878-75-70

nsk.coder

unread,
Aug 8, 2009, 12:47:08 PM8/8/09
to SPb Haskell User Group
Yakov, спасибо за ресурс (не читаю увы..)!
Я не слишком силен в английском, почитаю еще повнимательней, однако,
эта проблема как я понял навскидку имеет два решения:

1. в будущем GHC 6.12 ее устранят

2. сейчас надо использовать пакет utf8-string
про который я как раз и сказал -- что в сборке 6.10.3 его нет!!
:-(


Yakov ZAYTSEV:

nsk.coder

unread,
Aug 8, 2009, 12:53:58 PM8/8/09
to SPb Haskell User Group
Iliya, а что значит любая Юниксовая?

Т.е., если у меня под Linux локаль будет koi-8, то проблемы с GHC
6.10.3 не будет?? Что-то я не верю...

Однако, perl например, распознает и автоматически (можно и
принудительно) любую локаль . Да много разных механизмов "борьбы" уже
в разных платформах и языках придумали с этим злом, я просто не ожидал
что тут появятся проблемы...


Iliya Kuznetsov:

Iliya Kuznetsov

unread,
Aug 8, 2009, 1:08:57 PM8/8/09
to spb...@googlegroups.com
Добрый день, nsk.coder,
отвечаю сразу на два твоих письма. В 6.12 устранят проблему с вводом/выводом в юникоде, а именно (из того блога на который ссылался Яков):

kuznetsov@kuznetsov-nb:~/1> cat 1.hs 
s = "λ"

main = do
    writeFile "test.txt" s
    s2 <- readFile "test.txt"
    print (s == s2)
kuznetsov@kuznetsov-nb:~/1> ghc 1.hs
kuznetsov@kuznetsov-nb:~/1> ./1
False
kuznetsov@kuznetsov-nb:~/1> cat test.txt 
�kuznetsov@kuznetsov-nb:~/1> locale
LANG=ru_RU.UTF-8
LC_CTYPE="ru_RU.UTF-8"
LC_NUMERIC="ru_RU.UTF-8"
LC_TIME="ru_RU.UTF-8"
LC_COLLATE="ru_RU.UTF-8"
LC_MONETARY="ru_RU.UTF-8"
LC_MESSAGES="ru_RU.UTF-8"
LC_PAPER="ru_RU.UTF-8"
LC_NAME="ru_RU.UTF-8"
LC_ADDRESS="ru_RU.UTF-8"
LC_TELEPHONE="ru_RU.UTF-8"
LC_MEASUREMENT="ru_RU.UTF-8"
LC_IDENTIFICATION="ru_RU.UTF-8"
LC_ALL=
kuznetsov@kuznetsov-nb:~/1> 

То есть сейчас по умолчанию readFile / writeFile неюникодны, но если их импортировать из UTF8 то будут юникодны. В 6.12 очевидно по умолчанию будут юникодны и приведённый в блоге пример будет выдавать True, хотя сейчас False и чтобы работать в юникоде приходится импортировать readFile/writeFile в виде:

import System.IO.UTF8 (readFile, writeFile)
import Prelude hiding (readFile, writeFile)

Это легко делать в своём коде но что делать с остальными библиотеками?

Я писал "В любой системе где локаль запуска юникодная, таких проблем нет.", эта такая локаль в которой ввод/вывод на экран идёт в UTF-8 (либо основываясь на другом варианте представления юникода). В винде идёт в OEM то есть для русских символов требуется преобразование при выводе на экран (не в файл!). Как я понимаю, там stdout в command.com по умолчанию в win1251, ну и вопрос -- почему GHC это должно заботить? и мой ответ -- это на совести каждой отдельной программы, делать такое преобразование-костыль.

Все "механизмы борьбы" лучше отбросить и перейти на юникод во всём, тогда бороться не надо.

В пайтоне, насколько я помню, просто всё по умолчанию в юникоде. В ядре windows тоже надо вызывать OEM2ANSI или наоборот для ввода\вывода в консоль.


8 августа 2009 г. 20:53 пользователь nsk.coder <nsk....@gmail.com> написал:



--
Iliya Kuznetsov
+7-916-878-75-70

nsk.coder

unread,
Aug 13, 2009, 12:12:54 PM8/13/09
to SPb Haskell User Group
Спасибо за подробности.
Но в общем, я ситуацию понял.

В данный момент, для указанной версии GHC (6.10.3) -- дела с
кодировками находятся в очевидном ауте, и способа как-то нормально
работать без танца с бубном -- нет.

Так, даже

import System.IO.UTF8 (readFile, writeFile)

мы сделать не можем за отутствием нужн библиотек, хотя вроде как они
должны быть там (наверно, поставить их несложно, однако, я не
попробовал)

Все остальное -- уже следствия . Хотя я не считаю, что программист
должен напрягаться и делать сам "костыли".
Платформа (и, возможно, язык) должна предоставлять механизмы работы с
разными кодировками и локалями. Это делает и perl, и Qt, и насколько я
представляю -- Java.

Меня очень напрягло требование оформлять строковые литералы в теле
программы только в UTF-8 (а почему тогда не в двубайтном Юникоде?).
Конечно, это можно обойти, но зачем такие сложности? Неужели нельзя
предоставить программисту самому определять кодировку в исходниках
(как например, это делается в указанных выше платформах типа Qt) в
проблемных случаях?

А примеры в приведенном блоге -- вообще юмор, не работает нигде -- ни
под Windows (с их проблемной консолью), ни под юниксовой UTF-8
консолью -- как у Вас и показано.

Надеюсь, исправят. А щас -- грустно.

Iliya Kuznetsov:
> Добрый день, nsk.coder,отвечаю сразу на два твоих письма. В 6.12 устранят

Iliya Kuznetsov

unread,
Aug 13, 2009, 12:53:02 PM8/13/09
to spb...@googlegroups.com
System.IO.UTF8 идёт в text кажется, в платформе по умолчанию есть. В крайнем случае cabal install text ставит всё за несколько секунд и очень просто, так как это не биндинги и ничего сишного компилировать не надо.

utf-8 общепринятая нынче на юниксах десктопная кодировка и при её использовании везде (в написании программ, файловой системе, вводе\выводе) проблем просто не возникает. Ну и ещё в xml это кодировка по умолчанию. Хаскельные программы с юникодом работать умеют.

Для перекодировок в зоопарк восьмибитных символов типа koi8r cp1251 достаточно установить (на винде -- не столь легко) iconv, который умеет всё преобразовывать во всё.

Думаю добавить в GHC возможность по BOM-сигнатуре анализировать вариант конкретного юникодного представления в исходном тексте достаточно легко, на крайняк можно сделать iconv с преобразованием (из любого в UTF8), кстати и в обсуждаемом случае это часть возможного решения (вторая часть -- преобразовывать из unicode в cp866 перед выводом).

Дела в индустрии с кодировками обстоят просто отлично, вот лет 10 назад с юникодом были очевидные проблемы и даже в линуксах для его установки требовались костыли и напильники, а лет через 5 наверное ютф8 будет по умолчанию везде (разумный компромисс между транспортом и представлением).


13 августа 2009 г. 20:12 пользователь nsk.coder <nsk....@gmail.com> написал:



--
Iliya Kuznetsov
+7-916-878-75-70

Maxim Taldykin

unread,
Aug 13, 2009, 3:07:45 PM8/13/09
to spb...@googlegroups.com
Пара замечаний:
- BOM -- это козни микрософта, лучше без него;
- вместо iconv вполне можно обходиться encodeString & decodeString из
utf8-string (по желанию подключаются дополнительные кодировки).

13 августа 2009 г. 20:53 пользователь Iliya Kuznetsov
(iliya.k...@gmail.com) написал:

Iliya Kuznetsov

unread,
Aug 14, 2009, 1:16:35 AM8/14/09
to spb...@googlegroups.com
А чем плох BOM? по-моему наоборот сплошные выгоды, за исключением конечно допиливания всех текстовых обработчиков кто про него не в курсе.

13 августа 2009 г. 23:07 пользователь Maxim Taldykin <jor...@gmail.com> написал:

Пара замечаний:
 - BOM -- это козни микрософта, лучше без него;
--
Iliya Kuznetsov
+7-916-878-75-70

Maxim Taldykin

unread,
Aug 14, 2009, 2:22:06 AM8/14/09
to spb...@googlegroups.com
Ну, в превую очередь хотя бы то, что в данном случае символ Byte Order
Mark используется совсем не для того, зачем он был придуман.
Во-вторых, в utf-8 этот символ вообще не имеет смысла.
Да и выгод никаких особо.


14 августа 2009 г. 9:16 пользователь Iliya Kuznetsov
(iliya.k...@gmail.com) написал:

nsk.coder

unread,
Aug 25, 2009, 12:22:44 PM8/25/09
to SPb Haskell User Group
Долго занимался экспериментами :-) Пишу отчет:

0. Задача: как побороть utf-зло в Haskell под windows-консолью (т.е.
как вернуться назад к ASCII или OEM однобайтовым кодировкам в вводе и
выводе и в строковых литералах).

1. Поставил для начала utf8-string (System.IO.UTF8 и т.п.).
Разумеется, под виндой это решения не дало.

2. Хаскелловская обертка над iconv (одноименный package)
устанавливаться не захотела -- хотя CYGWIN есть и все необходимые
библиотеки были установлены. Да и толку похоже от нее тоже было бы
мало -- так как на выходе-входе пришлось бы вручную сражаться с тойже
utf

3. попробовал как-то победить необходимость UTF-8 в строковых
литералах. Для GHC есть флаг -XOverloadedStrings который позволят
менять стандартные строковые литералы на свои. Своих у меня не
было :-), решил использовать Data.ByteString.Char8, однако, это
решения не дало.
Пишем в кодировке CP1251 текст:

import qualified Data.ByteString.Char8 as B
main = B.putStrLn "Привет мир!"

запускаем:

runghc -XOverloadedStrings test.hs

Опять таже ошибка: lexical error in string/character literal (UTF-8
decoding error)

Т.е. это зло пока победить не получилось :-(

Ладно, хотя бы ввод-вывод допилить!!

4. Для этого решил устанавливать package encoding 0.5.2 (щас уже есть
0.6) -- он умеет узнавать кодировку системы (оси??) и прозрачно менять
print, getContents, hPutStr, hPutStrLn, hGetContents, readFile,
writeFile и т.п.) -- это кроме тех же самых перекодировок туда-сюда,
что умеет делать iconv.

Пакет долго компилировался, но установился, однако, как раз под
Windows он не умеет узнавать "кодировку системы по умолчанию"!! Я
пытался разобраться с неявными параметрами (Implicit parameters)
типа :enc и внутренностями пакета encoding. Правда, ума не хватило...

Я написал автору пакета и он дал советы... В общем, заработало. Только
пакет пока(?) не поддерживает CP866 (консольную кодировку виндов в
России по умолчанию). Пришлось для эксперимента менять консольную
кодировку и шрифт консоли с растрового на ТруТайпный, после этого
такая вот прога заработала:

-----------------------------------------------
{-# LANGUAGE ImplicitParams #-}
import Prelude hiding (print)
import System.IO (stdout)
import System.IO.Encoding
import Data.Encoding.CP1251

s = "Русский язык"

main = do
let ?enc = CP1251
hPutStrLn (stdout) s

-----------------------------------------------

Ну вот вроде пока все.

nsk.coder

unread,
Aug 27, 2009, 3:36:28 PM8/27/09
to SPb Haskell User Group
Половину желаний исполнил -- я допилил пакет encoding-0.52 для работы
с кодировкой cp866, теперь можно делать нормальный вывод в виндовую
консоль (ну более-менее нормальный, скажем команда print нормально не
работает, но она не работает и в стандартном encoding'е, так как
использует show). Скрипт ниже работает корректно и ghci и в runghc, и
компилируется с опцией --make:

{-# LANGUAGE ImplicitParams #-}

-- текст скрипта в кодировке UTF-8

import Prelude hiding (print)
import System.IO (stdout)
import System.IO.Encoding

import Data.Encoding.CP866

s = "Русский язык"

main = do
let ?enc = CP866
hPutStrLn (stdout) s

(Меня, правда, сильно удивил размер exe-файла в 9 мегабайт)

Автору пакета послал предложение включить поддержку в будущие версии
encoding'а.

Вот теперь бы кто обяснил, как исхитрится сделать не UTF-ные, т.е.
однобайтные строковые литералы.

Кому надо, попробую выложить здесь патченный исходник пакета с
поддержкой CP866

nsk.coder

unread,
Aug 27, 2009, 3:38:44 PM8/27/09
to SPb Haskell User Group

nickela

unread,
Oct 7, 2009, 1:44:44 PM10/7/09
to SPb Haskell User Group
сори за оффтоп, но есть команда смены кодировки терминала, например:
chcp 65001
переводит терминал в UTF-8
здесь подробнее: http://anvarichn.livejournal.com/43752.html

nsk.coder

unread,
Oct 13, 2009, 12:29:16 AM10/13/09
to SPb Haskell User Group, nsk....@gmail.com
Спасибо за неожиданную возможность, я думал, что терминал Windows XP
не умеет работать в UTF-8 совсем.

Однако, проблему с кривой реализацией кодировок в Haskell это не
решает, только что проверил.

Без импорта библиотек

import Prelude hiding (writeFile, readFile, print, putStrLn)
import System.IO.UTF8

не работает совсем, с ними -- работает ужасно и криво.

Так, скрипт

import Prelude hiding (print)
import System.IO.UTF8

main = print "Привет мир!"

выдает:
"\1055\1088\1080\1074\1077\1090 \1084\1080\1088!"

Скрипт

import Prelude hiding (putStrLn)
import System.IO.UTF8

main = putStrLn "Привет мир!"

выдает еще смешнее:
runghc Hello2.hs
Привет мир!
мир!
!

Пока, реальным и корректным решение проблемы с Haskell является лишь
использование пакета encoding (см. выше), и кстати, вышло обновление,
учитывающие посланные автору пожелания и все (вроде) русские кодировки

Miguel

unread,
Oct 14, 2009, 4:14:51 AM10/14/09
to spb...@googlegroups.com
Что интересно, если запускать под ghci в emacs-е и включить
set-buffer-process-coding-system в utf-8, то работает совершенно
нормально.

2009/10/13 nsk.coder <nsk....@gmail.com>:

Reply all
Reply to author
Forward
0 new messages