某些特定 Windows 语言环境的奇怪行为:为什么,以及如何应对?
Weird behaviour of some specific Windows locales: why, and how to cope?
我编写了一个简单的 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>
Locale 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 和其他软件)接受,但不能在 C setlocale
或 C++ std::locale
中使用它们。奇怪的是,当将无效的语言环境名称传递给 std::locale
构造函数时,似乎有两种不同的失败模式。有时它会像默认用户区域设置一样被静默解释,有时会抛出异常。 xx_xx
和fre_fr
属于第一种,xxx_xxx
和fr_FR.ISO8859-1
属于第二种。我对此没有任何解释。
fr_fr
使用非 ASCII 千位分隔符(不间断的 space)。由于此语言环境使用的编码是 Latin-1,如果终端设置为处理 UTF-8,它将中断,因为此字符代码是 incomplete/invalid UTF-8 序列。 chcp 1252
解决问题。
我编写了一个简单的 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>
Locale 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 千位分隔符(不间断的 space)。由于此语言环境使用的编码是 Latin-1,如果终端设置为处理 UTF-8,它将中断,因为此字符代码是 incomplete/invalid UTF-8 序列。chcp 1252
解决问题。