Короче говоря: boost::locale
изменяет только глобальный объект локали C++, но не локаль C. stod
использует C-locale, а не глобальный объект C++-locale. std::locale
меняет оба: глобальный объект локали C++ и локаль C.
Вся история: std::locale
— тонкая штука, на которую приходится много отладки!
Начнем с класса C++ std::locale:
std::locale loc("de_DE.utf8");
std::cout<<loc.name()<<"\n\n\n";
создает немецкую локаль (если она доступна на вашей машине, иначе выкидывает), что приводит к de_DE.utf8
на консоли.
Однако он не меняет глобальный объект локали c++, который создается при запуске вашей программы и является классическим ("C"). Конструктор std::locale
без аргументов возвращает копию глобального состояния:
...
std::locale loc2;
std::cout<<loc2.name()<<"\n\n\n";
Теперь вы должны увидеть C
, если до этого ничего не испортило вашу локаль. std::locale("") сотворит магию и узнает предпочтения пользователя и вернет их как объект, без изменения глобального состояния.
Вы можете изменить локальное состояние с помощью std::local::global
:
std::locale::global(loc);
std::locale loc3;
std::cout<<loc3.name()<<"\n\n\n";
Конструктор по умолчанию на этот раз приводит к de_DE.utf8
на консоли. Мы можем восстановить глобальное состояние до классического, вызвав:
std::locale::global(std::locale::classic());
std::locale loc4;
std::cout<<loc4.name()<<"\n\n\n";
что должно снова дать вам C
.
Теперь, когда создается std::cout, он клонирует свою локаль из глобального состояния c++ (здесь мы делаем это со строковыми потоками, но это то же самое). Последующие изменения глобального состояния не влияют на поток:
//classical formating
std::stringstream c_stream;
//german formating:
std::locale::global(std::locale("de_DE.utf8"));
std::stringstream de_stream;
//same global locale, different results:
c_stream<<1.1;
de_stream<<1.1;
std::cout<<c_stream.str()<<" vs. "<<de_stream.str()<<"\n";
Дает вам 1.1 vs. 1,1
- первое классическое второе немецкое
Вы можете изменить локальный объект потока с помощью imbue(std::locale::classic())
Само собой разумеется, что это не меняет глобальное состояние:
de_stream.imbue(std::locale::classic());
de_stream<<" vs. "<<1.1;
std::cout<<de_stream.str()<<"\n";
std::cout<<"global c++ state: "<<std::locale().name()<<"\n";
и вы видите:
1,1 vs. 1.1
global c++ state: de_DE.utf8
Теперь мы подходим к std::stod
. Как вы можете себе представить, он использует глобальное состояние локали С++ (не совсем верно, потерпите меня), а не (частное) состояние cout
-потока:
std::cout<<std::stod("1.1")<<" vs. "<<std::stod("1,1")<<"\n";
дает вам 1 vs. 1.1
, потому что глобальное состояние по-прежнему "de_DE.utf8"
, поэтому первый синтаксический анализ останавливается на '.'
, но локальное состояние std::cout
по-прежнему "C"
. После восстановления глобального состояния получаем классическое поведение:
std::locale::global(std::locale::classic());
std::cout<<std::stod("1.1")<<" vs. "<<std::stod("1,1")<<"\n";
Теперь немецкий "1,1"
не анализируется должным образом: 1.1 vs. 1
Теперь вы можете подумать, что мы закончили, но есть еще кое-что - я обещал рассказать вам о std::stod
.
Рядом с глобальной локалью C++ есть так называемая (глобальная) локаль C (происходит из языка C, и ее не следует путать с классической локалью "C"). Каждый раз, когда мы меняли глобальную локаль C++, локаль C тоже менялась.
Получить/установить локаль C можно с помощью std::setlocale(...)
. Чтобы запросить текущее значение, выполните:
std::cout<<"(global) C locale is "<<std::setlocale(LC_ALL,NULL)<<"\n";
чтобы увидеть (global) C locale is C
. Чтобы установить локаль C, выполните:
assert(std::setlocale(LC_ALL,"de_DE.utf8")!=NULL);
std::cout<<"(global) C locale is "<<std::setlocale(LC_ALL,NULL)<<"\n";
что дает (global) C locale is de_DE.utf8
. Но что теперь является глобальной локалью С++?
std::cout<<"global c++ state: "<<std::locale().name()<<"\n";
Как и следовало ожидать, C ничего не знает о глобальной локали C++ и оставляет ее неизменной: global c++ state: C
.
Теперь мы больше не в Канзасе! Старые c-функции будут использовать C-локаль, а новая функция c++ — глобальный c++. Приготовьтесь к забавной отладке!
Что вы ожидаете
std::cout<<"C: "<<std::stod("1.1")<<" vs. DE :"<<std::stod("1,1")<<"\n";
сделать? std::stod
— это совершенно новая функция С++ 11, и она должна использовать глобальную локаль С++! Подумайте еще...:
1 vs. 1.1
Он получает правильный немецкий формат, потому что C-locale установлен на 'de_DE.utf8', и он использует старые функции в стиле C под капотом.
Просто для полноты std::streams
использует глобальную локаль С++:
std::stringstream stream;//creating with global c++ locale
stream<<1.1;
std::cout<<"I'm still in 'C' format: "<<stream.str()<<"\n";
дает вам: I'm still in 'C' format: 1.1
.
Редактировать. Альтернативный метод разбора строки без вмешательства в глобальную локаль и не отвлекаться на нее:
bool s2d(const std::string &str, double &val, const std::locale &loc=std::locale::classic()){
std::stringstream ss(str);
ss.imbue(loc);
ss>>val;
return ss.eof() && //all characters interpreted
!ss.fail(); //nothing went wrong
}
Следующие тесты показывают:
double d=0;
std::cout<<"1,1 parsed with German locale successfully :"<<s2d("1,1", d, std::locale("de_DE.utf8"))<<"\n";
std::cout<<"value retrieved: "<<d<<"\n\n";
d=0;
std::cout<<"1,1 parsed with Classical locale successfully :"<<s2d("1,1", d, std::locale::classic())<<"\n";
std::cout<<"value retrieved: "<<d<<"\n\n";
d=0;
std::cout<<"1.1 parsed with German locale successfully :"<<s2d("1.1", d, std::locale("de_DE.utf8"))<<"\n";
std::cout<<"value retrieved: "<<d<<"\n\n";
d=0;
std::cout<<"1.1 parsed with Classical locale successfully :"<<s2d("1.1", d, std::locale::classic())<<"\n";
std::cout<<"value retrieved: "<<d<<"\n\n";
Что только первое и последнее преобразования успешны:
1,1 parsed with German locale successfully :1
value retrieved: 1.1
1,1 parsed with Classical locale successfully :0
value retrieved: 1
1.1 parsed with German locale successfully :0
value retrieved: 11
1.1 parsed with Classical locale successfully :1
value retrieved: 1.1
std::stringstream может быть не самым быстрым, но имеет свои достоинства...
person
ead
schedule
13.01.2016