Getting familiar with standard C++ Locales
Стандартная библиотека C++ предлагает простой и мощный способ предоставления локальной информации. Это делается с помощью класса<std::locale>, контейнера, который содержит всю необходимую информацию о конкретной культуре, такую как шаблоны форматирования чисел, форматирование даты и времени, валюта, конвертация корпуса и т. Д.
Вся эта информация представлена гранями, специальными классами, полученными из базового класса<std::locale::facet>. Такие грани упакованы в класс<std::locale>и позволяют предоставлять произвольную информацию о местности. Класс<std::locale>сохраняет счетчики ссылок на установленных гранях и может быть эффективно скопирован.
Каждая грань, установленная в<std::locale>объект, может быть извлечена с помощью функции<std::use_facet>. Например, грань<std::ctype<Char>>предоставляет правила для преобразования регистра, так что вы можете преобразовать символ в верхний регистр, как это:
std::ctype<char> const &ctype_facet = std::use_facet<std::ctype<char> >(some_locale);
char upper_a = ctype_facet.toupper('a');
Объект локализации может быть встроен в<iostream>, чтобы он форматировал информацию в соответствии с локализацией:
cout.imbue(std::locale("en_US.UTF-8"));
cout << 1345.45 << endl;
cout.imbue(std::locale("ru_RU.UTF-8"));
cout << 1345.45 << endl;
Показать бы:
1,345.45 1.345,45
Вы также можете создать свои собственные грани и установить их в существующие локальные объекты. Например:
class measure : public std::locale::facet {
public:
typedef enum { inches, ... } measure_type;
measure(measure_type m,size_t refs=0)
double from_metric(double value) const;
std::string name() const;
...
};
И теперь вы можете просто предоставить эту информацию в местность:
std::locale::global(std::locale(std::locale("en_US.UTF-8"),new measure(measure::inches)));
Теперь вы можете распечатать расстояние в соответствии с правильным местоположением:
void print_distance(std::ostream &out,double value)
{
measure const &m = std::use_facet<measure>(out.getloc());
out << m.from_metric(value) << " " << m.name();
}
Эта техника была принята Boost. Локальная библиотека для обеспечения мощной и правильной локализации. Вместо того, чтобы использовать очень ограниченные грани стандартной библиотеки C++, он использует ICU под капотом, чтобы создать свои собственные гораздо более мощные.
Common Critical Problems with the Standard Library
В стандартной библиотеке существует множество проблем, препятствующих использованию ее полной мощности, и есть несколько дополнительных проблем:
- Установка глобальной локализации имеет плохие побочные эффекты.
Рассмотрим следующий код:
intmain()
{
std::locale::global(""));
std::ofstream csv"test.csv";
csv<< 1.1<<","<< 1.3<< std::endl;
Каково будет содержаниеtest.csv? Это может быть «1.1,1.3» или «1,1,1,3», а не то, что вы ожидали.
Более того, это влияет даже наprintfи библиотеки, подобныеboost::lexical_cast, давая неправильное или неожиданное форматирование. На самом деле многие сторонние библиотеки разбиты в такой ситуации.
В отличие от стандартной библиотеки локализации, Boost. Locale никогда не меняет базовое форматирование чисел, даже когда он используетstdбэкэнды локализации, поэтому по умолчанию номера всегда отформатированы с использованием C-стиля locale. Для локализованного форматирования номеров требуются специальные флаги.
What would be the content of test.csv ? It may be "1.1,1.3" or it may be "1,1,1,3" rather than what you had expected.
More than that it affects even printf and libraries like boost::lexical_cast giving incorrect or unexpected formatting. In fact many third-party libraries are broken in such a situation.
Unlike the standard localization library, Boost.Locale never changes the basic number formatting, even when it uses std based localization backends, so by default, numbers are always formatted using C-style locale. Localized number formatting requires specific flags.
[ORIG_END] -->
- Форматирование номеров нарушается в некоторых местах.
Некоторые локали используют символ неразрывного пространства u00A0 для тысяч сепараторов, поэтому вru_RU.UTF-8локальном номере 1024 следует отображать как «1 024», где пространство является символом Unicode с кодовой точкой u00A0. К сожалению, многие библиотеки не справляются с этим правильно, например, GCC и SunStudio отображают символ «\xC2» вместо первого символа в последовательности UTF-8 «\xC2\xA0», который представляет эту точку кода, и фактически генерируют недействительный UTF-8.
- Местные названия не стандартизированы. Например, под MSVC нужно указать имя
en-USилиEnglish_USA.1252, когда на платформах POSIX оно будетen_US.UTF-8илиen_US.ISO-8859-1.
Более того, MSVC вообще не поддерживает UTF-8.
- Многие стандартные библиотеки предоставляют только локализации C и POSIX, поэтому GCC поддерживает локализацию только под Linux. На всех других платформах попытка создать локализации, отличные от «C» или «POSIX», не увенчалась бы успехом.