textpub.neocities.org . [ записки: обучаюсь C++ ]

Из моих записок про самообучение C++. Дата первой публикации: 13.02.2019.

Консольная программа выдает кракозябры 4

Третий способ решения проблемы

Начало можно прочитать в этой записи, вторая часть, начало, продолжение и окончание третьей части. Если во второй части мы подстраивали выводимый в командную строку Windows 7 (cmd.exe) текст под кодировку из группы кодировок OEM (866), в третьей части — настраивали из нашей программы кодировку командной строки под нужную нам кодировку из группы кодировок ANSI (1251), то здесь рассмотрим работу с Юникодом.

Юникод

Юникод — это стандарт, состоящий из двух частей: универсального набора символов (UCS — universal character set) и семейства кодировок (UTF) — способов представления символов из UCS в компьютере. Примеры последних: UTF-16, UTF-32, UTF-8. Юникод возник в 1991 году и продолжает развиваться: последняя на данный момент 11-я версия вышла в июне прошлого 2018 года.

Юникод синхронизируется с международным стандартом ISO/IEC 10646, который разрабатывается параллельно и имеет название «Universal Coded Character Set» или сокращенно «UCS». В этом стандарте свое семейство кодировок, например: UCS-2 (является подмножеством UTF-16), UCS-4 (совпадает с UTF-32).

Метка порядка байтов (BOM — byte order mark). В кодировке UCS-2, к примеру, любой символ представляется двумя байтами. Как уже упоминалось в первой части, для кодирования латиницы хватает первых 256 кодов, то есть одного байта. Программистам, пишущим для англоязычной аудитории, таким образом, было достаточно получить значение только одного из двух байт, которыми кодируются символы, так как второй байт для латиницы всегда побитно содержит нули. Таким программистам было выгодно, чтобы байт с кодом шел первым, а байт с нулями — вторым. Остальным же программистам нужно было читать байты в обычном порядке. Поэтому для первых была создана кодировка UCS-2 BE, а для вторых — UCS-2 LE. Аббревиатуры BE и LE означают Big-Endian (сначала старший байт, затем младший) и Little-Endian (сначала младший байт, затем старший).

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

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

Кодировка исходных файлов с текстом программы

Во второй части я уже писал о том, что «Visual Studio Community 2017» умеет работать с текстами программ в разных кодировках, и рассказывал, как это делается в подробностях.

В «Visual Studio Community 2017» можно работать с текстами программ в следующих кодировках Юникода (порядок перечисления и орфография сохранены такими же, как в выпадающем списке интегрированной среды разработки):

Под «сигнатурой» имеется в виду метка порядка байтов (BOM), про которую уже писалось выше.

При этом следует учитывать, что возможность работы с перечисленными кодировками в редакторе текстов «Visual Studio Community 2017» не означает то, что компилятор этой среды примет тексты программ во всех этих кодировках.

На сайте компании Microsoft сказано, что тексты программ в Юникоде в исходных файлах для компилятора среды могут быть в следующих кодировках:

Я пробовал произвести сборку в «Visual Studio Community 2017» приведенной ниже программы в кодировке UTF-7 и получил ошибку компилятора C2059. В кодировке UTF-8 без сигнатуры (то есть без метки BOM) сборка прошла успешно, но, очевидно, компилятор не воспринял текст программы как текст в Юникоде, потому что русские буквы вывелись кракозябрами.

Итак, компилятор и редактор текста «Visual Studio Community 2017» без проблем работают с кодировками UCS-2 LE с меткой BOM, UCS-2 BE с меткой BOM и UTF-8 с меткой BOM.

Переключение стандартных потоков ввода/вывода в режим Юникода

Такое переключение осуществляется с помощью функции _setmode (описание на сайте компании Microsoft). Пример программы:

#include <io.h> // для функции _setmode
#include <fcntl.h> // для константы _O_U16TEXT
#include <iostream>
using namespace std;
int main()
{
    // переключение стандартного потока вывода в формат Юникода
    _setmode(_fileno(stdout), _O_U16TEXT);

    wcout << L"У каждой эпохи свой язык\n"; // UTF-8 с сигнатурой
    return 0;
}

Добавим в нашу программу ввод в Юникоде:

#include <io.h> // для функции _setmode
#include <fcntl.h> // для константы _O_U16TEXT
#include <iostream>
using namespace std;
int main()
{
    wchar_t str[80]; // для входящей строки
    
    // переключение стандартного потока вывода в формат Юникода
    _setmode(_fileno(stdout), _O_U16TEXT);
    // переключение стандартного потока ввода в формат Юникода
    _setmode(_fileno(stdin), _O_U16TEXT);

    wcout << L"У каждой эпохи свой язык\n"; // UTF-8 с сигнатурой
    wcout << L"In    : "; wcin.getline(str, 80);
    wcout << L"Out   : " << str << L"\n";

    return 0;
}

Эта программа при языке системы (system locale) «Русский (Россия)» работает и при точечном (растровом) шрифте Terminal в окне консоли, и при юникодном шрифте Lucida Console (или Consolas). После смены языка системы на «Английский (США)» работает правильно (без кракозябр) только при выбранном в окне консоли одном из юникодных шрифтов — Lucida Console или Consolas. Подробно о переключении языка системы и шрифтах консоли я писал в предыдущих записях.

textpub.neocities.org . [ записки: обучаюсь C++ ]