我写了一个简单的c++程序来测试可用的Windows区域设置。
#include <iostream>
#include <iomanip>
#include <locale>
int main(int argc, char* argv[])
{
const char* locName = (argc < 2) ? "" : argv[1];
std::locale loc (locName);
std::cout.imbue(loc);
std::cout << "Locale is " << loc.name() << 'n';
std::cout << std::fixed << std::setprecision(8);
std::cout << 12345654321 <<'n';
std::cout << 123456.54321 << 'n';;
return 0;
}
我用msvc19编译它。以下是一些测试结果:
c:Temp>.test
Locale is
12,345,654,321
123,456.54321000
c:Temp>.test C
Locale is C
12345654321
123456.54321000
一切顺利。
c:Temp>.test xx_xx
Locale is xx_xx
12,345,654,321
123,456.54321000
c:Temp>.test xxx_xxx
c:Temp>
区域xx_xx
不存在,xxx_xxx
也不存在,但是其中一个给出与默认区域设置相同的结果,而另一个冻结了流。好的,更多的测试…
c:Temp>.test en_us
Locale is en_us
12,345,654,321
123,456.54321000
c:Temp>.test de_de
Locale is de_de
12.345.654.321
123.456,54321000
c:Temp>
完美,这是应该的。但是…
c:Temp>.test fr_fr
Locale is fr_fr
12345654321
c:Temp>.test fre_fr
Locale is fre_fr
12,345,654,321
123,456.54321000
c:Temp>
什么?fr_fr
根本不会打印浮点数,但fre_fr
会(尽管,
和.
的角色显然颠倒了)。然而,它们应该是同一语言环境的别名!
c:Temp> python
>>> import locale
>>> locale.normalize('fr_fr')
'fr_FR.ISO8859-1'
>>> locale.normalize('fre_fr')
'fr_FR.ISO8859-1'
嗯…
c:Temp>.test fr_FR.ISO8859-1
c:Temp>
没有输出。
现在我在某个地方读到,不能在设置C或c++语言环境中使用编码后缀。我能理解(尽管这很烦人)。但是为什么fr_fr
(以及fr
、french
、fr_FR
和French_France
)的奇怪行为,以及我如何提前识别和避免这些有缺陷的语言环境?有趣的是,fr_be
和fr_lu
的行为与预期一致。
有两个独立的问题。
就Windows而言,fre_fr
和fr_FR.ISO8859-1
不是有效的区域设置名称。它们被一些第三方软件(Python和其他软件)接受,但不能在Csetlocale
或c++std::locale
中使用它们。奇怪的是,当向std::locale
构造函数传递无效的区域设置名称时,似乎有两种不同的失败模式。有时,它会像默认用户区域设置一样被静默地解释,有时会抛出异常。xx_xx
和fre_fr
是第一类,xxx_xxx
和fr_FR.ISO8859-1
是第二类。我对此无法解释。fr_fr
使用一个非ascii千位分隔符(一个不可中断的空格)。由于该区域设置使用的编码是Latin-1,如果终端设置为处理UTF-8,它将中断,因为该字符代码是一个不完整/无效的UTF-8序列。chcp 1252
解决了问题。