Информационные технологии Вестник Нижегородского университета им. H.H. Лобачевс кого, 20 11,№ 3(2), с. 301-307
УДК 681.3.06
ЛОКАЛИЗАЦИЯ КОНСОЛЬНЫХ ПРИЛОЖЕНИЙ В ЯЗЫКЕ C++
© 2011 г. В.Л. Тарасов
Нижегородский госуниверситет им. Н.И. Лобачевского
vl-tarasov@yandex.ru
Поступила в редакцию 04.03.2011
Рассматриваются методы локализации консольных приложений, существующие в языке C++, и их реализация в трех средах разработки - Visual Studio, C++Builder и Eclipse. Установлено, что возможность русификации стандартными средствами языка программирования полностью реализована только в Visual Studio. Даются рекомендации по методам локализации.
Ключевые слова: консольные приложения, локализация, C++, кодовые страницы, перекодировка, среда разработки.
При разработке консольных приложений возникает проблема их локализации (русификации), вызванная тем, что в средах разработки и выполнения программ используются разные кодовые страницы с разными кодами для русских букв.
Символы с кодами 0x00 - 0x7F во всех кодовых страницах одинаковы и образуют набор символов ASCII (American Standard Code for Information Interchange - американский стандартный код для обмена информацией). Символы с кодами 0x80 - 0xFF используются для кодирования символов национальных алфавитов. На рис. 1 показана вторая половина кодовой страницы Windows-1251 [1], которую использует операционная система Windows для России.
Код символа равен сумме шестнадцатеричных номеров строки и столбца. Консольные приложения выполняются в текстовых окнах, где используется кодовая страница СР866 [2] (рис. 2). В этих страницах для русских букв выделены разные диапазоны кодов, поэтому программа, созданная с применением кодовой страницы 1251, будет выводить вместо русских букв символы кодовой страницы 866, имеющие те же коды, что и русские буквы в странице 1251.
Однобайтовые схемы кодирования позволяют работать только с 256 символами, что гораздо меньше количества используемых символов, поэтому были развиты многобайтовые схемы кодирования, чтобы знаки могли быть представлены в 8-битовых, 16-битовых, 24-битовых,
00 01 02 03 04 05 06 07 08 09 ОА ОБ ОС 0D ОЕ OF
80 ъ 0402 г 0403 t 201А г 0453 гг 201Е 2026 t 2020 Ф 2021 € 20АС <L- ФФ 2030 Jb 0409 < 2039 Iь 040А К 040С Ті 040В ц 040F
90 5 0452 2018 { 2019 V, 201С гг 201D + 2022 2013 2014 m 2122 Jb 0459 > 203А 1Ь 045А К 045С ъ. 045В ц 045F
АО HBSP 00А0 У 040Е У 045Е J 0403 й 00А4 Ґ 04Э0 і і 00А6 § 00А7 Ё 0401 © 00А9 Є 0404 « 00АВ —і 00АС 00AD © 00АЕ Ï 0407
ВО О 00В0 ± 00В1 I 0406 І 0456 Ґ 0491 V- 00Е5 91 00В6 00В7 ё 0451 № 2116 0 0454 » ООВВ j 0459 S 0405 S 0455 I 0457
СО А 0410 Б 0411 В 0412 Г 0413 Д 0414 Е 0415 ж 041Є 3 0417 И 0413 Й 0419 К 041А Л 041В М 041С Н 041D О 041Е П 041F
DO Р 0420 С 0421 Т 0422 У 0423 Ф 0424 X 0425 ц 0426 Ч 0427 ш 0428 Щ 0429 Ъ 042А ы 042В Ь 042С э 042D ю 042Е Я 042F
ЕО а 0430 б 0431 В 0432 Г 0433 Д 0434 е 0435 Ж 0436 3 0437 И 0439 Й 0439 К 043А Л 043В М 043С Н 043D О 043Е П 043F
F0 р 0440 С 0441 Т 0442 У 0443 Ф 0444 X 0445 Ц 0446 Ч 0447 Ш 0448 Щ 0449 Ъ 044А Ы 044В Ь 044С Э 044D Ю 044Е Я 044F
Рис. 1. Кодовая страница Windows 1251
00 01 02 03 04 05 06 07 08 09 ОА ов ОС 0D ОЕ OF
80 A 0410 Б 0411 В 0412 г 0413 Д 0414 Е 0415 ж 0418 3 0417 И 0413 Й 0419 К 041А л 041В м 041С Н 041D О 041Е П 041F
90 P 0420 С 0421 Т 0422 У 0423 Ф 0424 X 0425 ц 0426 Ч 0427 Ш 0420 Щ 042Э ъ 042А Ы 042В Ь 042С э 042D ю 042Е Я 042F
AO a 4 б 0431 В 0432 Г 0433 Д 0434 е 0435 Ж 0436 3 0437 И 0433 Й 043Э К 043А Л 043В М 043С Н 043D О 043Е П 043F
BO 1 2Й2 2Й3 2502 2524 2561 11 2562 11 2556 1 2555 2563 2551 її 2557 J 255D Л 255С J 255В 1 2510
CO L 2514 _L 2534 Т 252С h 251С 2500 \ 253С h 255Е h 255F IL 255А ІГ 2554 X 256Э т 2566 о СМ 2550 JL 1Г 256С 2567
DO JL 2588 T 2584 Т 2565 IL 2559 L 2558 Г 2552 [Г 2553 256В =н 258А J 2518 Г 250С 1 2588 ■ 2584 1 258С 1 2590 ■ 2580
E0 P 0440 С 0441 Т 0442 У 0443 Ф 0444 К 0445 Ц 0446 Ч 0447 Ш 0448 Щ 0449 Ъ 044А Ы 044В Ь 044С Э 044D Ю 044Е Я 044F
F0 Ё 0401 ё 0451 Є 0404 Є 0454 Ї 0407 Ї 0457 У 040Е У 045Е о оово 221Э 00В7 Ы 221А № 2116 к 00А4 ■ 25А0 HBSP 00А0
Рис. 2. Кодовая страница OEM 866
или 32-битовых последовательностях. Стандарт Unicode (Юникод) для представления данных [3, 4] задаёт однозначное соответствие символов кодам - элементам кодового пространства, представляющим неотрицательные целые числа, которые называются кодовыми точками. Например, цифре 0 соответствует кодовая точка U+0030. В образцах кодовых страниц, приведенных на рисунках 1 и 2, коды символов в кодировке Юникод указаны под знаками, например, для русских букв отведен диапазон от U+0410 (заглавная А) до U+044F (малая буква я). Коды русских букв образуют последовательность, возрастающую в алфавитном порядке. Исключение составляют буквы Ё(и+0401) и e(U+0451), которые расположены вне общего алфавитного порядка. Доступные в среде Windows символы и их кодовые точки можно увидеть с помощью программы Таблица символов (запускается командой Пуск, Все программы, Стандартные, Служебные, Таблица символов).
Чтобы консольное приложение «заговорило» по-русски, нужно обеспечить преобразование кодировки символов к среде выполнения. Далее рассмотрены средства учета национальных особенностей (локализации), имеющиеся в языках C и C++.
Средства локализации языка C
Для учета особенностей, связанных со страной и языком, используются специальные среды, называемые локальными контекстами (locale - место действия) [5], которые включают набор параметров и функций, обеспечивающих
поддержку национальных и культурных стандартов.
Локальный контекст определяется строкой формата: язык[_зона[.код]]
Здесь язык - обозначение языка (например, английский, немецкий, русский), а зона - страна, в которой используется язык. Этот квалификатор позволяет поддерживать национальные стандарты для различных стран, использующих один язык. Квалификатор код определяет кодовую страницу.
В таблице приведены примеры строк, определяющих локальные контексты. Эти строки не стандартизированы, поэтому поддержка тех или иных локальных контекстов зависит от реализации.
Таблица
Имя Описание
C de_DE de_AT rus -s RUS russian I Russian ?-Russian_Russia Russian_Russia.125ll Russian_Russia.866 Используется по умолчанию: соглашения стандарта А^-С, английский язык немецкий язык (германия) немецкий язык (Австрия) Русский язык (Россия). кодовая страница по умолчанию Русский язык (Россия). кодовая страница 1251 Русский язык (Россия). кодовая страница 866
Средства языка C для работы с локальными контекстами объявлены в заголовке locale. Настройку программы на конкретный локальный контекст выполняет функция:
char *setlocale( int category, const char *locale );
Аргумент category определяет категорию функций, на которые setlocale оказывает влияние:
LC_ALL - все категории LC_COLLATE - сравнение и преобразование строк LC_CTYPE - обработка символов lc_monetary - форматирование денежных сумм lc_numeric - Форматирование чисел lc_time - форматирование времени
Аргумент locale является указателем на строку, задающую имя локального контекста. Если locale указывает на пустую строку, используется локальный контекст, использующий кодовую страницу, получаемую из операционной системы. Если locale равен нулю (NULL), действующий локальный контекст не изменяется.
При успешном завершении работы функция setlocale возвращает указатель на статически определенную строку с описанием локального контекста или нулевой указатель при неудаче.
Возможны несколько вариантов вызова setlocale, например:
setlocale(LC_ALL, "Russian") - настройка всех функций на Россию;
setlocale(LC_CTYPE, "Russian") - настройка функций обработки символов на Россию;
setlocale(LC_CTYPE, ".1251") - настройка функций обработки символов на кодовую страницу 1251.
Программа 1. Использование функции setlocale
Программа выводит русское слово, заданное непосредственно в программе и введенное с консоли (клавиатуры) при локальном контексте по умолчанию, локальном контексте с настройками из ОС и явно заданными локальными контекстами, использующими кодовые страницы и 1251.
#include <iostream>
#include <locale>
#include <conio.h> using namespace std; void main()
{
string program = "программа", console; cout << "Default locale is\t"
<< setlocale(LC_ALL, null) << endl; cout << "Enter the Russian word: "; cin >> console; cout << program << "\t"
<< console << endl;
// Установка локального контекста // с настройками из ОС cout << "OS locale is\t"
<< setlocale(LC_ALL, "") << endl;
}
cout << program << "\t"
<< console << endl; cout << "DOS locale is\t"
<< setlocale(LC_CTYPE, ".866") << endl; cout << program << "\t"
<< console << endl; cout << "Locale: "
<< setlocale(LC_CTYPE, ".1251") << endl; cout << program << endl; cout << "Locale: "
<< setlocale(LC_CTYPE, ".866") << endl; cout << console << endl; getch(); return 0;
При выполнении программы с консоли вводилось слово «Консоль». В среде Visual Studio получаем:
Default locale is C
Enter the Russian word: консоль
±ЁюуЁрььр OS locale is программа DOS locale is ±ЁюуЁрььр
консоль
Russian_Russia.1251
?r-6r<m
Russian_Russia.866
консоль
Locale: Russian_Russia.1251 программа
Locale: Russian_Russia.866 консоль
Видно, что по умолчанию устанавливается стандартный контекст C, при действии которого русские буквы из программы не выводятся, а введенные с консоли выводятся.
Вызов setlocale(LC_ALL, "") устанавливает локальный контекст с настройками ОС, где предусмотрено использование кодовой страницы 1251, в котором русские буквы из программы выводятся правильно, а введенные с консоли - нет.
Поскольку в консольном окне используется кодовая страница 866, для правильного вывода русских букв, введенных из консольного окна, нужно изменять локальный контекст на кодовую страницу 866 вызовом setlocale(LC_CTYPE, ".866"), но при этом русские буквы, заданные в программе, выводятся неправильно.
Таким образом, при выводе русских букв, заданных непосредственно в программе, следует устанавливать кодовую таблицу 1251, а перед выводом русского текста, введенного с клавиатуры, необходимо установить кодовую таблицу 866.
В среде C++ Builder программа выдает:
Default locale is C
Enter the Russian word: консоль
±ЁюуЁрььр консоль
OS locale is LC_MONETARY=Russian_Russia.866
LC_TiME=Russian_Russia.866
LC_NUMERIC=Russian_Russia.866
LC_COLLATE=Russian_Russia.866
LC_CTYPE=Russian_Russia.866
±ЁюуЁрььр КОНСОЛЬ
DOS locale is LC_CTYPE=Russian_Russia.866
±ЁюуЁрььр КОНСОЛЬ
Locale: LC_CTYPE=Russian_Russia.1251
±ЁюуЁрььр
Locale: LC_CTYPE=Russian_Russia.866 КОНСОЛь
Здесь, так же как в Visual Studio, локальным контекстом по умолчанию является C, но локальным контекстом, устанавливаемым по
настройкам ОС, является Russian_Russia.866, в то время как в Visual Studio это
Russian_Russia.1251. Сведения о локальном контексте здесь выводятся подробно с указанием всех категорий. Установка локального контекста на Russi an_Russi a.1251 не обеспечивает правильного вывода из программы русских букв.
Та же программа, построенная в среде Eclipse, выводит:
Default locale is C Enter the Russian word: консоль
±ËюyËpььp OS locale is ±ËюyËpььp DOS locale is ±ËюyËpььp
консоль
Russian_Russia.1251
консоль
Russian_Russia.866
консоль
Locale: Russian_Russia.1251 ±ËюyËpььp
Locale: Russian_Russia.866 консоль
Таким образом, в C++ Builder и Eclipse не реализовано преобразование символов из кодовой страницы 1251, используемой в среде разработки, в кодовую страницу 866 среды выполнения программ. Правильно выводятся только строки русского текста, вводимые из консольного окна.
Средства локализации языка C++
В языке C++, так же как в C, используется концепция локального контекста, включающего настройки обработки символов, форматирования чисел, даты, времени, денежных величин. Локальный контекст реализуется библиотечным классом locale, объявленным в заголовке locale. Используя класс locale, можно создать в программе несколько объектов локальных контекстов и использовать их по мере необходимости.
В классе locale есть несколько конструкторов, создающих объекты локальных контекстов.
Конструктор по умолчанию locale() создает объект локального контекста, являющийся копией глобально назначенного локального контекста на момент конструирования.
Конструктор, аргументом которого является пустая строка - locale(""), создает объект локального контекста с параметрами из настроек операционной системы.
Аргументом конструктора класса locale может быть строка, описывающая локальный контекст, такая же, как аргумент функции setlocale.
Статическая функции global класса locale устанавливает заданный локальный контекст как глобальный, после чего он оказывает влияние на все аспекты локализации.
Метод name класса locale возвращает строку типа string с именем локального контекста.
Программа 2. Использование класса locale
Программа аналогична программе 1, но используются средства C++ для управления локальными контекстами.
#include <iostream>
#include <locale>
#include <conio.h> using namespace std; int main()
{
// Создание копии действующего по умолчанию // локального контекста locale def_loc;
// Вывод имени локального контекста cout << "Default locale is\t"
<< def_loc.name() << endl; string program = "программа", console; cout << "Enter the Russian word: "; cin >> console; cout << program << "\t"
<< console << endl;
// Создание локального контекста // с установками из ОС locale os_l oc (""); cout << "OS locale is\t"
<< os_loc.name() << endl;
// Установка для использования лок. контекста locale::global (os_loc); cout << program << "\t"
<< console << endl;
// Создание локального контекста для России // с кодовой страницей 866 для вывода русских // букв, вводимы с консоли в кодировке 866 locale Output866("Russian_Russia.866");
// Установка для использования лок. контекста l ocale::global(Output866); cout << "Locale is\t"
<< Output866.name() << endl;
cout << program << "\t"
<< console << endl;
// Создание локального контекста для России // с кодовой страницей 1251 для вывода русских // букв, заданных в программе в кодировке 1251 locale Output1251("Russian_Russia.1251");
// Установка для использования лок. контекста locale::global (Output1251); cout << "Locale is\t"
<< Output1251.name() << endl; cout << program << "\n";
// повторное использование объекта лок. контекста locale::global(Output866); cout << "Locale is\t"
<< Output866.name() << endl; cout << console << endl; getch(); return 0;
}
Таким образом, использование класса locale дает те же результаты, что и использование функции setlocale языка C.
Нестандартные функции настройки консоли
Кроме локализации, возможность которой заложена в стандартах языков C и C++, существуют нестандартные средства. В Windows имеются функции для управления консольным окном DOS, для доступа к которым в программу следует включить заголовочный файл Win-dows.h.
Функция
UINT GetConsoleCP(void);
При запуске в среде Visual Studio программа выводит:
Default locale is C Enter the Russian word: консоль
±ЁюуЁрььр OS locale is программа Locale is ±ЁюуЁрььр Locale is программа Locale is консоль
консоль
Russian_Russia.1251
Tr^r^
Russian_Russia.866
консоль
Russian_Russia.1251
Russian_Russia.866
Видно, что русские буквы, заданные в программе, правильно выводятся при установке для использования кодовой страницы 1251, а для правильного вывода русских букв, вводимых с консоли, должна быть установлена кодовая страница 866.
В средах C++ Builder и Eclipse не реализовано преобразование кодировок, то есть русский текст, заданный непосредственно в программе, не выводится правильно на консоль. Удается вывести только вводимые с консоли русские буквы. Далее приведен вывод программы, созданной в Eclipse. Вывод программы, созданной с использованием C++Builder, аналогичен, но более подробен и поэтому не приводится.
Default locale is C Enter the Russian word: консоль
±ЁюуЁрььр OS locale is ±ЁюуЁрььр Locale is ±ЁюуЁрььр Locale is ±ЁюуЁрььр Locale is консоль
консоль
C
консоль
Russian_Russia.866
консоль
Russian_Russia.1251
Russian_Russia.866
извлекает входную кодовую страницу, используемую консолью, ассоциированной с запущенным процессом (программой) для преобразования клавиатурного ввода в соответствующие символьные значения. Возвращаемое значение является номером кодовой страницы. Тип возвращаемого значения UINT определен в заголовочном файле windef.h инструкцией
typedef unsigned int UINT;
Функция
BOOL SetConsoleCP(UiNT wCodePageiD);
устанавливает кодовую страницу, используемую консолью, ассоциированной с запущенным процессом, при вводе символов с клавиатуры. Параметр wCodePageiD является идентификатором устанавливаемой кодовой страницы. При успешном завершении возвращается ненулевое значение. Если работа функции неудачна, возвращается нуль.
Функция
UINT GetConsoleOutputCP(void);
извлекает выходную кодовую страницу, используемую консолью, ассоциированной с вызванным процессом для перевода символьных значений, записываемых различными выводящими функциями в изображения символов, показываемые в консольном окне. Возвращает номер кодовой страницы.
Функция
BOOL SetConsoleOutputCP (UINT wCodePageiD);
устанавливает выходную кодовую страницу, используемую консолью, ассоциированной с
вызванным процессом. Параметр wCodePageiD является идентификатором устанавливаемой кодовой страницы. При успешном завершении возвращается ненулевое значение.
Программа 3. Настройка консоли
Программа использует функции для управления кодовыми страницами при вводе с консоли и при выводе на консоль.
#include <windows.h>
#include <iostream>
#include <conio.h> using namespace std; void main()
{
cout << "GetConsoleCP() =\t"
<< GetConsoleCP() << endl; cout << "GetConsoleOutputCP() =\t"
<< GetConsoleOutputCP() << endl;
// кодовая страница 1251 для ввода из консоли SetConsoleCP(1251);
// кодовая страница 1251 для вывода на консоль SetConsoleOutputCP(1251); char Name[100]; cout << "Введите ваше имя\^'; cin >> Name;
cout << "Здравствуйте, \t\t" << Name << endl; getch() ;
}
В программе сначала получаются входные и выходные кодовые страницы, затем для ввода и вывода устанавливается кодовая страница 1251, которая обеспечивает корректный ввод и вывод русских букв.
Далее приведены результаты работы программы.
GetConsoleCP() = 866
GetConsoleOutputCP() = 866 Введите Ваше имя Владимир
Здравствуйте, Владимир
Видно, что по умолчанию для ввода с консоли и вывода на консоль устанавливается кодовая страница 866. Для согласования среды разработки и консоли следует установить для консольного ввода и вывода кодовую страницу 1251.
При использовании функций настройки консоли русские буквы будут отображаться правильно, если для консольного окна выбран шрифт Lucida Console. Для выбора шрифта нужно щелкнуть правой кнопкой мыши по заголовку окна, выполнить команду Свойства, а затем на вкладке Шрифт выбрать нужный шрифт.
Программа 3 работает одинаково как в среде Visual Studio, так и в средах C++Builder и Eclipse, но только тогда, когда для консоли используется оконный режим. При полноэкранном режиме в консольном окне русские буквы не отображаются.
Прямое преобразование символов
Для преобразования кодировки символов из кодовой страницы, действующей в среде разработки, в кодовую страницу, используемую в среде выполнения, можно вставить в программу заголовочный файл windows.h и использовать функцию:
BOOL CharToOem( LPCTSTR lpszSrc, LPSTR lpszDst );
Здесь lpszSrc - указатель на строку для перевода, lpszDst - указатель на буфер для преобразованной строки.
Функция CharToOem существует в двух вариантах: для однобайтовых (ANSI) и для широких символов. Если функция CharToOem используется как ANSI функция, строка может быть переведена на месте установкой для параметров lpszDst и lpszSrc одинакового значения. Это не может быть сделано, если CharToOem используется как функция для обработки широких символов.
В качестве аргументов функции CharToOem можно передавать обычные строки символов с нулевым байтом на конце. Возвращаемое функцией CharToOem значение всегда не нуль, за исключением случая, когда передаются одинаковые адреса для lpszSrc и lpszDst при использовании версии функции для широких символов. В этом случае функция возвращает нуль.
Программа 4. Использование CharToOem
Далее приведен пример использования функции CharToOem. Для удобства использования написана вспомогательная функция Rus, возвращающая указатель на строку, преобразованную в кодировку OEM.
#include <iostream>
#include <windows.h>
#include <conio.h> using namespace std; char* Rus(char* s)
{
static char buf[500];
CharToOem(s, buf); return buf;
}
int main()
{
cout << RusC'^ Вас зовут?\Ґ'); char Name[100]; cin >> Name;
cout << Rus("3дравствуйте,\t"); cout << Name << endl; getch(); return 0;
}
Пример работы программы:
как Вас зовут? Владимир Здравствуйте, Владимир
При вводе строки инструкцией cin >> Name;
используется кодовая страница 866 консольного окна, поэтому при обратном выводе на консоль строки Name ее не требуется преобразовывать для правильного отображения русских букв.
В средах C++ Buider и Eclipse данная пограмма работет точно так же.
Выводы
Заложенная стандартами языков C и C++ возможность русификации консольных прило-
жений реализована только в Microsoft Visual Studio. Нестандартные средства локализации, реализованные в ОС Windows, обеспечивают локализацию и работают одинаково во всех рассмотренных средах разработки. Выбор средства локализации остается за программистом, но использование функции CharToOem представляется менее удобным, так как эту функцию приходится вызывать при выводе каждой созданной в программе строки с русскими буквами. Более удобным представляется локализация, реализуемая путем настройки консоли с использованием функций SetConsoleCP и SetConsoleOutputCP, продемонстрированная в программе 3, которая переносима между средами разработки.
Список литературы
1. Windows 1251 [Электронный ресурс]. - Режим
доступа: http://msdn.microsoft.com/ru-ru/goglobal/
cc305144
2. OEM 866 [Электронный ресурс]. - Режим доступа: http://msdn.microsoft.com/ru-ru/goglobal/
cc305166
3. The Unicode Consortium [Электронный ресурс]. - Режим доступа: http://www.unicode.org
4. Юникод - Википедия [Электронный ресурс]. -
Режим доступа: http: http://ru.wikipedia. org/wiki/
Unicode
5. Джосьютис Н. C++ Стандартная библиотека. Для профессионалов. СПб.: Питер, 2004. 730 с.
LOCALIZATION OF CONSOLE APPLICATIONS IN C++ LANGUAGE
V.L. Tarasov
The methods of localization of console applications that exist in the language C ++, and their implementation in the three development environments - Visual Studio, C ++ Builder and Eclipse are considered. It is established that the possibility of Russification of the standard language C++ has been fully realized only in Visual Studio. Some recommendations on localization methods are given.
Keywords: console applications, localization, C++, code pages, recoding, development environment.