Кириллица в консоли

Думаю, нет нужды говорить о том, что обучаться программированию имеет смысл на примерах с консольным интерфейсом. Не требующая специфических знаний в области GUI-библиотек, стандартизованная для всех операционных систем, имеющая встроенную поддержку в стандартных библиотеках большинства языков, консоль – идеальный вариант.

И C++ – не исключение. Поддержки графического интерфейса пользователя в стандартной библиотеке нет, и не предвидится ее включение из-за особенностей философии языка.

Часто возникающей проблемой с консольными программами (у русских программистов) является вывод кириллицы. Точнее его невозможность.

Отметим также, что проблема эта возникает исключительно у Windows-программистов. Имя проблеме – кодировки.

Если вы о них “что-то слышали, но что это такое – не уверены”, очень советую прочитать эту статью.

Нет, правда, настоятельно советую.

Даже если нет времени – распечатайте и прочитайте в автобусе.

Автор статьи – Джоэл Спольски. Если вы не знаете, кто это такой – срочно узнайте, и больше никогда никому не говорите, что не знаете, кто такой Джоэл Спольски. Быть программистом и не знать, кто такой Джоэл Спольски, – нонсенс.

Но ближе к делу.

Дело в том, что в русскоязычных версиях Microsoft Windows в качестве основной кодировки принята ANSI, которая стандартизована одноименным комитетом, и для разных языков надстраивает ASCII-символы разными кодовыми страницами. Для нашего Отечества это cp1251. Консоль же Windows в противоположность использует устаревшую OEM-кодировку, на наречии Microsoft – кодовую страницу cp866. Зачем – Бог знает. Наверное, чтобы мы могли насладиться программами, написанными лет 20 назад, с красивыми окошками и кнопочками в стиле DOS.

Из-за несоответствия кодировок (т.е. способов интерпретации кодов символов в их графические представления) мы и видим на экране “╧ЁштхЄ!” вместо “Привет!”.

Если вы чего-то не поняли из предыдущего объяснения, рекомендую все же прочитать статью по ссылке выше, поскольку здесь – только краткое описание проблемы, а исторический фон и установление причинно-следственных связей – материал столь обширный, что дублировать его я не вижу смысла.

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

Есть в Си волшебная функция

char* setlocale (int category, const char* locale)

Она устанавливает в программе т.н. локаль (locale) – набор национальных параметров, включающий помимо форматов даты, времени, валюты и т.п., характерных для данной местности, кодировку символов, в ней употребляемую.
Идентификатор локали – строка. Ее формат определяется по-разному. Рассмотрим различные варианты применительно к России.

1) В Win32API это число 1049. Логично, черт возьми.

2) В Java, .NET и Windows начиная с версии 6.0 (“Vista”) используется более стандартизованный способ представления локали – строка формата

язык-РЕГИОН

Применительно к нам – “ru-RU”.

3) В POSIX-системах (UNIX, GNU/Linux) используется стандартизованный формат

язык_регион.кодировка

В нашем случае – “Russian_Russia.1251″

А кто сказал, что будет легко?

Вернемся к функции setlocale(). Первый параметр – целое число, в качестве передаваемого значения обычно используют константы, объявленные в <clocale> и говорящие, какие категории нам надо локализовывать. Нам подойдет константа LC_CTYPE, которая влияет на функции, имеющие дело с символами.

Второй параметр функции – идентификатор локали. Туда можно передавать следующие значения:

1) Сокращение – “rus”.

2) Название языка полностью – “Russian”.

3) Номер кодовой страницы – “.1251″.

Примечание: Если передать номер используемой в консоли OEM-кодировки (“.866″), то символы в программе будут к ней преобразованы, что также является решением нашей проблемы. Этого же можно добиться, передав строку “.OCP”, которая означает OEM Code Page и устанавливает локаль с текущей OEM-кодировкой операционной системы.

4) Идентификатор в формате POSIX – “Russian_Russia.1251″

5) Пустую строчку – “”. Если мы так поступим, в программе будет установлена кодовая страница операционной системы по умолчанию, в нашем случае это соответствует cp1251.

Т.е. установка локали в простейшем случае производится следующим образом:

setlocale(LC_CTYPE, "");

Полноценный пример:

#include<iostream>
#include<clocale>

using namespace std;

int main()
{
setlocale(LC_CTYPE, "");
cout << "Русский текст?! Да ладно!" << endl;
cin.get();
return 0;
}

Ссылки для дальнейшего самосовершенствования:
Список различных кодировок (Википедия)
Описание функции setlocale() (англ.)
Способы преобразования кодировок в C++
Сайт Майкрософта. Кодировки OEM 866 и Windows 1251

Комментарии (11)

Рубрика: Стандартная библиотека, Ядро, Язык

11 комментариев на «Кириллица в консоли»

  1. Спасибо за статью, в тему) Не сталкивались случайно с вопросом узнавания наличия желаемой кодировки у пользователя в *NIX?

    P. S. Первый линк не открывается :/

  2. aksenov

    За статью на здоровье :)
    Первый линк у меня открывается, странно… Пробовал в разных браузерах.

    Узнавание наличия локали:

    try
    {
    locale WeHave(“russian”);
    cout << WeHave.name().c_str() << endl;
    }
    catch(runtime_error& e )
    {
    cout << e.what() << endl;
    }

    По-моему, класс locale должен быть стандартизован, и, следовательно, может использоваться и под никсами, и не под ними.

  3. aksenov

    А вообще в Линуксе благодаря тотальной поддержке прекрасной кодировки utf-8 ничем из описанного в статье заниматься не приходится.
    Просто виндовая консоль подгадила :/

    В одном из следующих постов разберем Юникод.

    • alexey

      С UTF-8 свои заморочки. Дело в том, что в памяти символы занимают разное количество байтов (английские буквы, пунктуация и т. п. – 1, а большинство других, русские буквы в том числе – 2). При этом ввод-вывод строк проблем не вызывает, а вот попытка разобрать строку на отдельные символы приводит к появлению вопросиков (они же квадратики).

  4. Спасибо за рецепт!

    Поддержка UTF-8 действительно стала широко распространена, но я столкнулся с прогой, выводившей сообщения на русском в KOI8-R, так как в организации, где она была написана, использовалась такая кодировка. У себя-то я исходники конвертнул и скомпилировал, но подумалось, что это не тру путь при создании программ :)

    Сегодня у меня тоже сайт открылся, не знаю что за глюк тогда был…

  5. Уведомление: Текущие время и дата « C++ для людей

  6. Спс, хоть всё я это и знаю, но почитать не отказался). Вот только про utf-8 поподробней плыз))

  7. aksenov

    Да, все руки не доходят этим заняться. В течение лета, думаю, напишу.

  8. Василий

    Благодарю, друг, от имени всех начинающих! :-)

  9. Вова

    Видел кучу ответов на форумах, но почему-то нигде не заметил одного интересного варианта. Наверное плохо искал.
    system(“echo Текст на русском”);

    должно всё выводить по-русски.

  10. Андрей

    Касательно всей подоплеки кодировок и т.д. я знал и до того, а вот как записать это в СИшке еще не знал :) За это и спасибо :)

Добавить комментарий

Fill in your details below or click an icon to log in:

Логотип WordPress.com

You are commenting using your WordPress.com account. Log Out / Изменить )

Фотография Twitter

You are commenting using your Twitter account. Log Out / Изменить )

Фотография Facebook

You are commenting using your Facebook account. Log Out / Изменить )

Connecting to %s