Шаг 531.
Библиотека STL.
Интернационализация. Использование локальных контекстов

    На этом шаге мы рассмотрим некоторые особенности использования локальных контекстов.

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

    Согласно стандартам 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). В Германии, как и в России, для отделения дробной части от целой используется запятая.

    На следующем шаге мы продолжим изучение этого вопроса.




Предыдущий шаг Содержание Следующий шаг