textpub.neocities.org . [ записки: обучаюсь C++ ]
Из моих записок про самообучение C++. Дата первой публикации: 15.01.2019. Дата последней редакции: 13.02.2019.
Собрав и запустив свою первую консольную программу на C++ под Windows (см. предыдущую запись), начинающий сталкивается с еще одной проблемой: вместо осмысленной фразы по-русски (с латиницей этой проблемы нет) на выходе получается нечитаемый текст кракозябры (по-английски «mojibake»).
Текст нашей простейшей программы по Лафоре:
#include <iostream> using namespace std; int main() { cout << "У каждой эпохи свой язык\n"; return 0; }
Результат работы этой программы при запуске из командной строки Windows (я работаю под «Windows 7 Профессиональная» с установленным SP1):
Так как компьютер воспринимает только числа, программистам для работы с текстом изначально пришлось представить буквы и знаки препинания числами. Для этого были составлены таблицы соответствия символов числовым кодам. Одной из самых известных таких таблиц является ASCII, разработанная и стандартизированная в США в 1963 году.
Эта таблица включает 128 значений, потому что технически для представления символов в распоряжении программистов на тот момент было лишь 7 бит (27 = 128). Так как кроме, собственно, прописных и строчных букв, цифр и знаков препинания в таблицу нужно было включить управляющие символы и различные специальные символы, то закодировать можно было только символы одного алфавита. А раз дело происходило в США, то и алфавит, ествественно, взяли латинский.
Позже для представления символа в компьютере стали применять 8 бит (один байт) и число значений в таблице стало возможным расширить до 256 значений (28 = 256). Появились расширенные таблицы ASCII, первые 128 значений (коды 0-127) которых совпадали с изначальной таблицей ASCII и содержали латинский алфавит, а во второй части (коды 128-255) программисты-пользователи других алфавитов кодировали свой алфавит.
Из-за того, что поначалу не существовало единого стандарта, даже для одного алфавита могло быть создано и распространено несколько несовместимых вариантов расширенных таблиц ASCII. Например, даже сегодня в Windows 7 еще поддерживаются две группы кодировок OEM и ANSI. Кодировки из этих групп являются вариациями расширенной таблицы ASCII. (Кстати, в литературе по этой теме можно запутаться в терминах-синонимах. По-моему, кодовая страница (code page), набор символов (character set) и кодировка (character encoding) это примерно одно и то же.)
В итоге для решения проблемы путаницы кодировок была придумана универсальная кодировка, ставшая известной под названием Юникод (Unicode). Первая версия стандарта Юникода вышла в 1991 году. Первые 128 значений в таблице Юникода совпадают с ASCII, точно так же, как и в расширенных таблицах ASCII. Однако, в Юникоде для кодирования символов используется больше 8 бит, поэтому кодовое пространство единой таблицы состоит из более миллиона позиций, в которые удалось поместить все известные алфавиты мира. В идеале все программы должны использовать Юникод, но тут есть две проблемы: 1) сам Юникод имеет несколько форм представления (UTF-8, UTF-16, UTF-32 и так далее) и разные несовместимые реализации в разных программах, что создает путаницу; 2) производители операционных систем хотят поддерживать и старые программы, вместо Юникода использующие не универсальные старые кодировки.
Итак, вернемся к нашей проблеме. В Windows 7 для программ, не работающих с Юникодом, в реестре назначены кодировки по умолчанию: для программ, работающих в текстовом интерфейсе из группы кодировок OEM; для программ, работающих в графическом интерфейсе из группы кодировок ANSI. Конкретные номера кодовых страниц зависят от «языка системы» (он же «system locale»). Не путать с «языком интерфейса» (он же «display language» или «interface language»).
Язык системы можно поменять через «Панель управления Часы, язык и регион Язык и региональные стандарты вкладка «Дополнительно» раздел «Язык программ, не поддерживающих Юникод». Например, при выбранном языке системы «Английский (США)» кодовые страницы по умолчанию для программ с текстовым интерфейсом (группа кодировок OEM) и программ с графическим интерфейсом (группа кодировок ANSI) будут 437 и 1252 соответственно. Для нашего языка системы «Русский (Россия)» соответствующие идентификаторы кодовых страниц 866 и 1251.
То есть для интерпретатора командной строки Windows 7 (cmd.exe) кодовая страница по умолчанию в данном случае 866. Нашу же программу мы набираем в среде «Visual Studio Community 2017», которая по умолчанию сохраняет текст программы в кодовой странице 1251. Так как первые части (коды 0-127) у этих кодировок (это уже отмечалось выше) совпадают, а именно в этих частях закодирован латинский алфавит, то фраза, выводимая латиницей, будет отображена правильно. Фраза на русском языке, как в нашем случае, отображается кракозябрами, потому что вторые части кодовых страниц 866 и 1251 (коды 128-255), в которых закодирован русский алфавит, не совпадают.
Таких способов предлагается три. Перечислю в порядке увеличения сложности:
1) привести исходный текст программы в кодировку 866 (дополнительно рассмотрены функции перекодировки CharToOemBuff и OemToCharBuff);
2) оставить исходный текст программы в кодировке 1251, добавить в программу операторы, указывающие командной строке выводить текст в кодировке 1251 (в том числе с помощью функции setlocale, с помощью функций SetConsoleOutputCP и SetConsoleCP и с помощью функции system и команды chcp);
3) настроить командную строку для работы с Юникодом, привести исходный текст программы в кодировку Юникода, добавить в программу операторы, указывающие командной строке выводить текст в кодировке Юникода рассмотрено тут.
Подробнее эти способы разберем в следующих записях.