На этом шаге мы рассмотрим некоторые особенности использования локальных контекстов.
Полноценная интернационализация обычно не ограничивается преобразованием текстовых сообщений. Например, необходимо также позаботиться об использовании разных стандартов форматирования чисел, денежных величин и дат. Если некоторая функция работает с буквами, она должна на основании данных локального контекста обеспечить корректную обработку всех букв данного языка.
Согласно стандартам POSIX и Х/Ореn, локальный контекст может задаваться и в программах языка С. Для этой цели применяется функция setlocale(). Смена локального контекста влияет на работу функций, зависящих от классификации и преобразования символов (например, isupper() и toupper()), а также функций ввода-вывода (таких, как printf()).
Однако подход, реализованный в С, ограничен. Поскольку локальный контекст является глобальным свойством, одновременное использование нескольких локальных контекстов (например, чтение вещественных чисел по английским стандартам и вывод их в немецком стандарте) либо невозможно, либо требует относительно больших усилий. Кроме того, локальные контексты С не расширяются. Они обладают только теми возможностями, которые заложены в них реализацией. Если потребуется реализовать какую-нибудь новую возможность, придется использовать другой механизм. Наконец, механизм С не позволяет определять новые локальные контексты для поддержки специальных культурных стандартов.
Стандартная библиотека C++ решает все перечисленные проблемы при помощи объектно-ориентированного подхода. Прежде всего, строение локального контекста инкапсулируется в объекте типа locale, что позволяет использовать несколько локальных контекстов одновременно. Операции, зависящие от локальных контекстов, настраиваются на применение соответствующих объектов. Например, с каждым потоком данных для ввода-вывода можно ассоциировать объект локального контекста, который задействуется различными функциями потока данных для соблюдения соответствующих стандартов. В следующем примере показано, как это делается:
//--------------------------------------------------------------------------- #include <vcl.h> #include <iostream> #include <locale> #include <conio.h> //необходимо для getch() #pragma hdrstop //--------------------------------------------------------------------------- #pragma argsused using namespace std; std::string ToRus(const std::string &in) { char *buff = new char [in.length()+1]; CharToOem(in.c_str(),buff); std::string out(buff); delete [] buff; return out; } int main (int argc, char* argv[]) { // Использование классического локального контекста C // для чтения данных из стандартного ввода cin.imbue(locale::classic()); // Использование немецкого локального контекста // для записи данных в стандартный вывод cout.imbue(locale("de_DE")); // Чтение и запись вещественных чисел в цикле double value; while (cin >> value) { cout << value << endl; } getch(); return 0; } //---------------------------------------------------------------------------
Результат работы программы приведен на рисунке 1.
Рис.1. Результат работы приложения
Показанная ниже команда ассоциирует "классический" локальный контекст С со стандартным каналом ввода:
cin.imbue(locale::classic());
В этом локальном контексте форматирование дат и чисел, классификация символов и т. д. выполняются так же, как в исходном языке С без назначения локальных контекстов. Следующее выражение возвращает соответствующий объект класса locale:
std::locale::classic()
Тот же результат будет получен при использовании выражения:
std:: locale("C")
Это выражение конструирует объект locale с заданным именем. Специальное имя "С" является единственным именем, которое заведомо поддерживается реализацией C++. Поддержка любых других локальных контекстов не гарантирована, хотя предполагается, что язык C++ поддерживает другие локальные контексты.
Соответственно следующая команда ассоциирует локальный контекст de_DE со стандартным каналом ввода:
cout.imbue("locale("de_DE"));
Разумеется, команда выполняется успешно только в том случае, если этот локальный контекст поддерживается системой. Если при конструировании объекта локального контекста будет указано неизвестное имя, генерируется исключение runtime_error.
Если все прошло успешно, то входные данные читаются по классическим правилам С (например, 47.11), а выходные данные выводятся по немецким стандартам (47,11). В Германии, как и в России, для отделения дробной части от целой используется запятая.
На следующем шаге мы продолжим изучение этого вопроса.