C++ stod不能与boost::locale一起正常工作

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和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 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-locale
stod
使用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::流使用全局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