C++ stod不能与boost::locale一起正常工作
我试图在德语区域设置中同时使用boost::locale和std::stod,其中逗号是十进制分隔符。考虑这个代码:C++ stod不能与boost::locale一起正常工作,c++,boost,locale,boost-locale,C++,Boost,Locale,Boost Locale,我试图在德语区域设置中同时使用boost::locale和std::stod,其中逗号是十进制分隔符。考虑这个代码: boost::locale::generator gen; std::locale loc(""); // (1) //std::locale loc = gen(""); // (2) std::locale::global(loc); std::cout.imbue(loc); std::string s = "1,1"; //float string in g
boost::locale::generator gen;
std::locale loc(""); // (1)
//std::locale loc = gen(""); // (2)
std::locale::global(loc);
std::cout.imbue(loc);
std::string s = "1,1"; //float string in german locale!
double d1 = std::stod(s);
std::cout << "d1: " << d1 << std::endl;
double d2 = 2.2;
std::cout << "d2: " << d2 << std::endl;
如我所料。当我注释掉第(1)行和取消注释第(2)行时,输出是
d1: 1,1
d2: 2,2
d1: 1
d2: 2.2
d2的结果是可以预期的。据我所知,boost::locale希望我显式地指定d2应格式化为一个数字,并执行以下操作
std::cout << "d2: " << boost::locale::as::number << d2 << std::endl;
不过,我不知道它为什么会这样 长话短说:
boost::locale
仅更改全局c++-locale对象,而不更改c-localestod
使用C-locale而不是全局C++-locale对象std::locale
同时更改:全局c++-locale对象和c语言环境
整个故事:
std::locale
是一件微妙的事情,需要进行大量调试
让我们从C++类STD::LoaLe:
开始 std::locale loc("de_DE.utf8");
std::cout<<loc.name()<<"\n\n\n";
现在您应该看到C
,如果之前没有任何东西弄乱您的区域设置。std::locale(“”)将发挥一些神奇的作用,找出用户的首选项并将其作为对象返回,,而无需更改全局状态
您可以使用std::local::global
更改本地状态:
std::locale::global(loc);
std::locale loc3;
std::cout<<loc3.name()<<"\n\n\n";
这将再次为您提供C
现在,当创建STD::CUT时,它从全局C++状态克隆它的区域(这里我们用StrueFipe来做,但它是一样的)。全局状态的后续更改不会影响流:
//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
global c++ state: de_DE.utf8
现在我们来看std::stod
。可以想象,它使用全局C++区域(不完全正确,忍受我)状态,而不是<代码> CUT< /COD>流:(私有)状态:
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语言环境也都发生了变化。 可以使用
std::setlocale(…)
获取/设置C语言环境。要查询当前值,请运行:
std::cout<<"(global) C locale is "<<std::setlocale(LC_ALL,NULL)<<"\n";
它产生(全局)C语言环境是de_de.utf8
。但现在的全球C++环境是什么?
std::cout<<"global c++ state: "<<std::locale().name()<<"\n";
怎么办<代码> STD::STOD 毕竟是全新的C++ 11功能,它应该使用全局C++语言环境!再想想
1 vs. 1.1
它正确地使用了德语格式,因为C语言环境设置为“de_de.utf8”,并且在引擎盖下使用了旧的C风格函数
为了完整性,
std::stringstream stream;//creating with global c++ locale
stream<<1.1;
std::cout<<"I'm still in 'C' format: "<<stream.str()<<"\n";
以下测试显示:
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";
std::stringstream可能不是最快的,但有它的优点…如果您通过提供指向std::stod(str,idx)的idx指针来检查解析错误,那么从长远来看,它将为您节省很多痛苦。是的,我在我的真实代码中这样做,但在这个简单的测试中删除了它。很好!我不知道这个项目有多大,但改变全球区域设置有可能破坏其他地方的东西,这应该是最后的手段。。。但是当您使用std::stod时,您没有选择的优点。但话说回来,我有什么选择?我的用户希望软件读取带有浮点数的csv文件。所以要么我使用用户的语言环境,要么他必须显式地指定十进制分隔符。在后一种情况下,我将不得不完全放弃使用std::stod之类的工具,因为我当然不想一直更改全局语言环境。一开始看似简单的事情,最后却变成了一团乱麻……:-(如果速度不是最关键的要求,你可以考虑使用STD::StrugSt流(查看我的更新)。其他快速选项将是Booo::ListalySkyCost,但是如果我记得正确的话,它也取决于全局状态(但是不要把它当作Global)。感谢您的精彩描述!虽然它并没有完全回答我的问题,但现在我自己只需要几步就可以找到它。设置std::locale(“”)正如您所描述的,全局语言环境确实成功地将C语言环境更改为Windows下的de_de.utf8,而不是de_de.utf8,而是German_German.1252。但是,全局设置boost语言环境不会更改C语言环境,这对我来说破坏了stod!因此,唯一的解决方案是不显式引用某些语言环境名称,这似乎与indows和*nix系统,而且我事先也不知道,正在设置两次区域设置
std::cout<<"(global) C locale is "<<std::setlocale(LC_ALL,NULL)<<"\n";
assert(std::setlocale(LC_ALL,"de_DE.utf8")!=NULL);
std::cout<<"(global) C locale is "<<std::setlocale(LC_ALL,NULL)<<"\n";
std::cout<<"global c++ state: "<<std::locale().name()<<"\n";
std::cout<<"C: "<<std::stod("1.1")<<" vs. DE :"<<std::stod("1,1")<<"\n";
1 vs. 1.1
std::stringstream stream;//creating with global c++ locale
stream<<1.1;
std::cout<<"I'm still in 'C' format: "<<stream.str()<<"\n";
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