из разных письменностей: в документах Unicode могут соседствовать русские, латинские, греческие буквы, китайские иероглифы и математические символы. Кодовые страницы становятся ненужными.
Общие сведения
Коды в Unicode разделены на несколько областей. Область с кодами от 0000 до 007F содержит символы набора Latin 1 (младшие байты соответствуют кодировке ISO 8859-1). Далее идут области, в которых расположены знаки различных письменностей, а также знаки пунктуации и технические символы; часть кодов зарезервирована для использования в будущем. Символам кириллицы выделены коды в диапазоне от 0400 до 0451.
Для работы с документами Unicode нужны соответствующие шрифты. Как правило, файл шрифта Unicode содержит начертания не для всех символов, определенных в стандарте, а лишь для символов из некоторых областей.
Unicode в Windows NT
Ядро Windows NT, ее графический интерфейс (GDI) и файловая система NTFS реализованы с использованием Unicode. Программы, запущенные в среде NT, могут работать также с однобайтовыми символами, кодировка которых в этом случае соответствует установленной по умолчанию кодовой странице ANSI (для России - Windows Cyrillic, или CP 1251).
Перед вызовом некоторых функций программного интерфейса NT программы, работающие с кодовой страницей ANSI, преобразуют однобайтовые символы в Unicode. Чтобы преобразование выполнялось без ошибок, пользователь должен правильно указать страну в приложении Regional Settings. Это необходимо также для корректной работы с национальными символами программ, запущенных в сеансе MS-DOS; они используют кодовую страницу OEM, которая не всегда совпадает со страницей Windows (в частности, для России это CP 866).
Unicode в Windows 95
В отличие от Windows NT, ядро и графический интерфейс Windows 95 не используют Unicode, а работают с кодовыми страницами. Однако в этой ОС предусмотрена возможность динамического изменения наборов символов и раскладок клавиатуры, что позволяет создавать документы, содержащие одновременно символы из разных наборов. Буфер обмена Windows 95 способен хранить тексты в формате CF_UNICODETEXT.
В составе Windows 95 поставляется набор шрифтов Unicode, с которыми, в частности, могут работать программы Microsoft Office 97.
Исследование шрифтов Unicode
Выяснить, какие наборы символов присутствуют в том или ином шрифте Unicode, можно с помощью стандартной утилиты Character Map (таблица символов), включенной в состав Windows NT (аналогичная программа из Windows 95 не подходит). Запустите ее и выберите в списке Font шрифт Arial, а в списке Subset - набор символов Windows Characters.
Получившаяся таблица будет повторять кодовую страницу ANSI с символами западноевропейской латиницы. Выбрав же в списке Subset строку Cyrillic, вы увидите таблицу с символами кириллицы и без символов латиницы, соответствующую кириллической области Unicode.
Для программ, работающих с кодовыми страницами ANSI, вместо шрифта Arial будет подставлен (в зависимости от страницы, установленной в системе) Arial Cyr, Arial Baltic, Arial CE, Arial Greek и т. д.
Чтобы увидеть, как происходит подстановка, запустите программу regedt32.exe и на странице HKEY_LOCAL_MACHINE on Local Machine в разделе SOFTWAREMicrosoftWindows NTCurrent Version раскройте папку Fonts. Вы убедитесь, что шрифтов, подобных Arial Cyr, там нет. Это виртуальные шрифты, которые создаются специально для программ, работающих с кодовыми страницами ANSI. Раскрыв папку FontSubstitutes того же раздела, можно определить, что, например, шрифт Arial Cyr образуется из шрифта Arial с использованием таблицы преобразования 204.
Техника, применяемая в Windows 95, аналогична описанной с одним отличием: шрифты для подстановки определяются не в Реестре, а в файле win.ini (раздел FontSubstitutes).
Работа с символами Unicode
Использование Unicode значительно упрощает создание многоязычных приложений. Поэтому, создавая программы с прицелом на этот стандарт, вы закладываете неплохую базу для локализации своего программного продукта. Ниже мы опишем работу с символами Unicode в среде Windows на языке Си (примеры проверялись с пакетом Microsoft Visual C++ 4.0 под управлением Windows NT 4.0 и Windows 95).
Определение символьных данных Unicode
Переменные
Для обычных символьных данных - символов ANSI - в языке Си используется тип char; переменная этого типа занимает в памяти один байт. Для хранения символов Unicode необходимо два байта, из-за чего их называют широкими символами (wide characters). В языке Си им соответствует тип wchar_t, с которым связаны макрокоманды WCHAR и TCHAR, описанные в файле winnt.h. При определении данных Unicode рекомендуется пользоваться не непосредственно типом wchar_t, а макрокомандами.
Макрокоманда WCHAR определена:
typedef wchar_t WCHAR;
Макрокоманда TCHAR универсальная, она позволяет определять как символы Unicode, так и символы ANSI. Если в начале текста программы имеется инструкция #define UNICODE, препроцессор преобразует запись TCHAR в wchar_t, если нет - в char:
#ifdef UNICODE typedef wchar_t WCHAR; typedef WCHAR TCHAR; #endif
В файле winnt.h содержатся также макрокоманды для определения указателей на символы Unicode и универсальные макрокоманды (на базе TCHAR), с помощью которых можно создавать указатели на символы как ANSI, так и Unicode. Эти макрокоманды обеспечивают корректную работу при выполнении арифметических операций над указателями и при использовании указателей для индексации массивов символов Unicode.
Модифицируя исходный текст своей программы для работы с Unicode, замените типы данных char, char*, а также LPSTR и LPCSTR соответствующими макрокомандами для Unicode. Кроме того, проверьте, не пользуетесь ли вы где-нибудь тем обстоятельством, что символ занимает в памяти один байт, и если да, внесите необходимые исправления.
Константы
Литеральные константы, соответствующие символам Unicode, записываются как обычные символьные константы, только с префиксом L:
char chStar = '*'; /* однобайтовый символ */ WCHAR chStar = L'*'; /* символ Unicode */ char szStar[] = ''Star''; /* строка однобайтовых символов */ WCHAR szStar[] = L''Star''; /* строка символов Unicode */
Универсальная макрокоманда TEXT позволяет определять символьные константы как двух- или однобайтовые в зависимости от того, работает ли программа с Unicode или нет:
TCHAR chStar = TEXT('*'); TCHAR szStar[] = TEXT(''Star'');
При наличии в тексте программы инструкции #define UNICODE такая запись будет преобразована в определение константы Unicode (с префиксом L), а при ее отсутствии - в определение обычной символьной константы.
Специальные символы
Символьная строка ANSI закрывается нулевым байтом, а строка Unicode - 16-разрядным нулем. Для обозначения конца такой строки можно использовать запись TEXT(' ').
Код символов CR (возврат каретки) и LF (перевод строки) в Unicode соответственно 0x000D и 0x000A (в кодировке ASCII - 0x0D и 0x0A); не заменяйте их последовательность на символ с кодом 0x0D0A. В Unicode определены также символы разделения строк (0x2028) и абзацев (0x2029).
Символы с кодами 0xFEFF и FFEF служат сигнатурами текстовых файлов Unicode; о них мы подробнее расскажем в конце статьи.
Функции Win32 и Unicode
Большинство прототипов функций Win32, получающих в качестве параметров текстовые строки или символы, реализованы как макрокоманды. В зависимости от наличия в тексте программы инструкции #define UNICODE препроцессор Си подставляет вместо имени макрокоманды имя функции с суффиксом W (для Unicode) или A (для однобайтовых символов).
Благодаря этому большинством стандартных библиотечных функций Cи можно пользоваться в программах, работающих с Unicode, точно так же, как в программах, работающих с символами ANSI. Это, в частности, относится к функциям для работы со строками, такими как strcat и printf.
Однако есть и исключения; например, функции atoi и atol всегда ожидают строку символов ANSI, так что перед обращением к ним строка Unicode должна быть преобразована. И разумеется, во всех случаях необходимо следить за правильностью передачи параметров.
Рассмотрим в качестве примера известную функцию MessageBox, которая выводит на экран простейшую диалоговую панель с сообщением. В файле winuser.h для нее имеется два прототипа - MessageBoxA и MessageBoxW.
Текст программы MsgUni, работающей с Unicode и вызывающей функцию MessageBox, приведен в листинге.
Вывод диалоговой панели с русским текстом в кодировке Unicode
// Файл MsgUni.c // #define UNICODE #include "windows.h" TCHAR szAppName[] = TEXT(''Приложение MsgUni''); TCHAR szAppTitle[] = TEXT(''Привет!''); //char szAppTitle[] = ''Привет!''; int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { MessageBox(NULL, szAppTitle, szAppName, MB_OK); return 0; }
Если же заменить корректное определение строковой константы
TCHAR szAppTitle[] = TEXT("Привет!");
на
char szAppTitle[] = "Привет!";
(в листинге оно закрыто знаком комментария), то MessageBox вместо ожидаемой строки Unicode получит строку ANSI и выведет в окне нечитаемый текст. Подобные ошибки часто возникают при модификации программ для работы с Unicode.
Вот некоторые функции программного интерфейса Win32, полезные при работе с Unicode.
Функции GetACP и GetOEMCP возвращают номера установленных в Windows кодовых страниц ANSI и OEM, которые нужны для корректного преобразования символов ANSI в Unicode.
Функция GetTextCharset сообщает идентификатор набора Unicode для заданного контекста отображения. Более подробную информацию о шрифте Unicode можно получить при помощи функции GetTextCharsetInfo.
Функции mbstowcs и MultiByteToWideChar преобразуют строку однобайтовых символов в строку Unicode, функции wcstombs и WideCharToMultiByte выполняют обратное преобразование.
(Oкончание в следующем номере.)
Об авторах: Братья Александр Вячеславович и Григорий Вячеславович Фроловы - авторы серий книг "Библиотека системного программиста" и "Персональный компьютер. Шаг за шагом". E-mail: frolov@glas.apc.org; http://www.glasnet.ru/~frolov