из разных письменностей: в документах 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