Вывод кириллицы в консоль на языке Nim
Обновление: с версии Nim 0.15.0 консоль по умолчанию выставляет кодировку stdout в utf-8 (см. issue #4417).
Есть такой замечательный язык — называется Nim, в котором всё хорошо, но есть лишь одна загвоздка: кириллица рисуется кракозябрами в консоли из-под Windows.
Почему так? Сейчас узнаем.
Возьмём простую программу, которая должна вывести слово «привет»:
echo "привет!"
Компилируем, запускаем, и видим:
$ nim c -r app.nim
╨┐╤А╨╕╨▓╨╡╤В!
Да, ошибаться невозможно, это точно они — кракозябры. От нашего сообщения в неизменном виде остался только знак восклицания.
Попробуем разобраться в чём дело — попросим компилятор Nim собрать нам исходники на C:
$ nim c -c app.nim
Теперь в папке nimcache
нас ждут файлы stdlib_system.c
и app.c
. Первый нам не интересен: это стандартная библиотека Nim. Посмотрим в app.c
как была сохранена наша строка «привет!». Ищем, после чего находим такое объявление:
STRING_LITERAL(TMP140, "\320\277\321\200\320\270\320\262\320\265\321\202!", 13);
Во что это превратилась наша строка? Я отвечу — это UTF-8. Компилятор Nim преобразовал нашу строчку в UTF-8, и поэтому консоль не может вывести символы правильно, потому что в ней установлена другая кодировка. Обычно под «русской виндой» установлена кодировка windows-1251. Всё, что нам нужно сделать — поменять кодировку консоли на UTF-8.
Лезем в Microsoft Developer Network, откапываем оттуда функции GetConsoleOutputCP
и SetConsoleOutputCP
, которые и позволят нам сменить кодировку:
when defined(windows):
proc setConsoleOutputCP(codepage: cint): cint
{.stdcall, dynlib: "kernel32", importc: "SetConsoleOutputCP".}
proc getConsoleOutputCP(): cint
{.stdcall, dynlib: "kernel32", importc: "GetConsoleOutputCP".}
Так как мы с вами не индусы, мы сначала запомним старую кодировку, установим свою, а когда выйдем из программы, то восстановим исходную:
when defined(windows):
let g_originalOutputCP = getConsoleOutputCP()
# 65001 - utf-8 codepage
discard setConsoleOutputCP(65001)
echo "привет!"
when defined(windows):
# Restore original codepage
discard setConsoleOutputCP(g_originalOutputCP)
Пробуем вывести нашу строчку "привет!".
$ nim c -r app.nim
привет!
Готово!
А вот и полный код:
when defined(windows):
proc setConsoleOutputCP(codepage: cint): cint
{.stdcall, dynlib: "kernel32", importc: "SetConsoleOutputCP".}
proc getConsoleOutputCP(): cint
{.stdcall, dynlib: "kernel32", importc: "GetConsoleOutputCP".}
let g_originalOutputCP = getConsoleOutputCP()
# 65001 - utf-8 codepage
discard setConsoleOutputCP(65001)
echo "привет!"
when defined(windows):
# Restore original codepage
discard setConsoleOutputCP(g_originalOutputCP)
Чтобы не парится, можно восстанавливать кодовую страницу через addQuitProc
.
Вот и всё!