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

Думаю, нет нужды говорить о том, что обучаться программированию имеет смысл на примерах с консольным интерфейсом. Не требующая специфических знаний в области 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

Реклама

26 комментариев

Filed under Стандартная библиотека, Ядро, Язык

26 responses to “Кириллица в консоли

  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. Андрей

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

  11. Спасибо, все легко и просто!!!

  12. Уведомление: Кириллица в консоли C++ « Words are DUST

  13. Огромное спасибо, ваш пост помог разобраться с проблемой кодировок в виндовой консоли. Я полагаю, что другой способ сделать то же самое (без использования сишных библиотек) такой:
    #include
    std::locale::global(std::locale(«»));

  14. Сергей

    ахах, у меня доктор веб на endl ругается хЪ короче, спасибо за помощь, всё робит )

  15. Евгений

    Статья — бальзам на душу. Полезный гайд.

  16. Виктор

    В компиляторе Dev-C++ даже «Полноценный пример» из статьи не работает — выходит крякозябру.

    • Dimir

      Наверное, вы имели в виду компилятор MinGW, а не среду разработки Dev-C++

      • Виктор

        Да, я имел ввиду среду разработки Dev-C++ и соответственно входящий в состав дистрибутива компилятор MinGW. Такая формулировка как то меняет подход к озвученной мною проблеме?

        • Виктор

          Могу переформулировать: компилятор, входящий в состав среды разработки Dev-C++, «Полноценный пример» из статьи компилируется, но на выходе — та же «╧ЁштхЄ!» вместо «Привет!»

    • Виктор

      После некоторых поисков в глобальной сети и проб, выяснилось, что setlocale(LC_CTYPE, «»); работает в Win7, а в консоли Windows XP категорически отказывается работать.
      Альтернативный костыль в пару строк:
      #include
      #include // необходима для CharToOem()

      using namespace std;
      char str[80]; // для примера взято 80
      int main()
      {
      CharToOem(«Привет»,str);
      cout << str << endl;
      system("PAUSE");
      return 0;
      }

      • Александр

        Не представляю в чем шутка но:
        При:
        setlocale(LC_ALL, » «);
        cout<> b; -вводим русские буквы
        cout << b; -выводит введенные русские буквы корректно

        при:
        setlocale(LC_ALL, "");

        cout<> b; -вводим русские буквы
        cout << b; -выводит кракозябру вместо введенных русских букв.

        Win XP, MS Visual C++ 2010
        У меня начинается паника…

        • Александр

          Разобрался

          Прописываю:
          setlocale(LC_ALL, «»);

          Для вывода данных из кода использую:
          wcout<<L"…";

          С cin и cout обрабатывающими данные пользователя и константы типа "Привет!" проблем не возникает.

          Спасибо всем, кто пытался вникнуть в текст который я писал выше.

  17. Если говорить о C++, то у этого метода есть один недостаток — невозможность работы с классом string. Я решил проблему, сохранив исходник с кодировкой 866.

  18. Уведомление: Програмирование С++ | ipo2012

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

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход /  Изменить )

Google+ photo

Для комментария используется ваша учётная запись Google+. Выход /  Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход /  Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход /  Изменить )

w

Connecting to %s