13.2 读取数字
问题
如何以一种遵循本地传统的格式化方式把一个数字写到一个流中去?这种方式可能相互不同,并且依赖于你所在的位置。
解决方案
就像在示例13-2中那样,使用当前的locale来贯通当前你正在写入的流,或者使用你可以设置这个全局的locale然后再创建一个流。讨论部分将讨论后者。
示例13-2 使用本地格式写入数字
#include <iostream>
#include <locale>
#include <string>
using namespace std;
// There is a global locale in the background that is set up by the
// runtime environment. It is the "C" locale by default. You can
// replace it with locale::global(const locale&).
int main() {
locale loc(""); // Create a copy of the user's locale
cout << "Locale name = " << loc.name() << endl;
cout.imbue(loc); // Tell cout to use the formatting of
// the user's locale
cout << "pi in locale " << cout.getloc().name() << " is "
<< 3.14 << endl;
}
讨论
示例13-2说明了该如何使用用户的locale来格式化一个浮点型数字。这样做需要两步,创建一个本地locale类的实例,然后再把它与流相关联。
作为开始,示例13-2创建loc,它是这个用户的locale的副本。你不得不以一个空字符串来使用locale的构造函数来这样做(并不是这个默认的构造函数),如下所示:
locale loc("");
这个区别是微妙的但是很重要,后面将会来讨论。按照这种方式来创建一个locale对象创建这个用户的locale的副本,这个用户的locale是实现时定义的。这就意味着如果一台机器配置的是使用美国英语,locale::name()将返回一个locale字符串,如“en_US”、“English_United States.1252”、“english-american”等。这些事实上的字符串是实现时定义的,并且C++标准的唯一要求就是“C”。
为了比较,locale的默认构造函数返回当前全局locale的副本。对一个运行的C++程序,只有单个全局的locale对象(在一些运行库中,可能是作为一个静态变量实现的 —— 精确地说,如何实现是实现定义的)。默认的情况下,它是C locale,并且你可以使用locale::global(locale& loc)来代替它。当流被创建时,它们使用这个全局的locale,这就意味着,cin、cout、cerr、wcin和wcerr都是使用C locale,因此如果你需要使用遵循某个特定的locale传统的格式时,你必须显式地改变它。
Locale的名字是非标准化的。通常,它们看上去与下面的相同:
<language>_<country>.<codepage>
这里语言或许是一个全名,如西班牙语“Spanish”,或者是两个字符的代码,如“sp”;国家或者是国家的全名,如“Colombia”;或者是两个字符的国家代码,如“CO”,并且代码页就是这个代码页,如1252。语言是唯一的必须部分。在各种系统中,使用显式的locale来做试验,在不同的编译器上搞清楚不同名字的意义。如果你使用的locale名字是不合法的,它将抛出一个运行时错误。示例13-3中给出了几个显式的locale名字的例子。
示例13-3 显式地命名locale
#include <iostream>
#include <fstream>
#include <locale>
#include <string>
using namespace std;
int main() {
try {
locale loc("");
cout << "Locale name = " << loc.name() << endl;
locale locFr("french");
locale locEn("english-american");
locale locBr("portuguese-brazilian");
cout.imbue(locFr); // Tell cout to use French formatting
cout << "3.14 (French) = " << 3.14 << endl;
cout << "Name = " << locFr.name() << endl;
cout.imbue(locEn); // Now change to English (American)
cout << "3.14 (English) = " << 3.14 << endl;
cout << "Name = " << locEn.name() << endl;
cout.imbue(locFr); // Tell cout to use Brazilian formatting
cout << "3.14 (Brazil) = " << 3.14 << endl;
cout << "Name = " << locBr.name() << endl;
}
catch (runtime_error& e) {
// If you use an invalid locale name, a runtime_error exception
// is thrown.
cerr << "Error: " << e.what() << endl;
}
}
这个程序在Visual C++ 7.1编译的Windows平台上的输出如下所示:
Locale name = English_United States.1252
3.14 (French) = 3,14
Name = French_France.1252
3.14 (English) = 3.14
Name = English_United States.1252
3.14 (Brazil) = 3,14
Name = Portuguese_Brazil.1252
你可以看到我的机器的locale是使用代码页1252的美国英语。这个例子也说明了在很多其他的locale也使用pi。注意,法国和巴西使用一个逗号代替小数点。千位分割符是也是不同的:法语和葡萄牙语使用空格符来代替逗号分割符,因此,在美国英语中的1 000 000.25在法语和葡萄牙语中写法为1 000 000,25。
在大部分情况下,你都应该避免显式地使用名字来创建locale。对于使用locale来打印数字、日期、货币或者其他别的东西,你应该简单地使用一个空字符串来实例化一个locale,然后用它来调用imbue成员函数填充你的流。
locale的行为可能还是有点难以理解,因此我总结了几个重要的关键点:
?默认的全局locale是“C”locale,因为它是在每一个实现中由标准得以保证的。
?在程序开始时,标准流都是使用全局locale即“C”locale来创建的。
?通过传递一个空字符串给这个locale构造函数,如locale(""),你就可以创建一个用户运行时的locale的副本。
?通过传递一个标识locale的字符串,你就可以创建一个命名的locale对象,如locale("portuguese-brazilian")。尽管这个字符串是没有标准化的。
?一旦你有一个代表用户的默认的locale对象或者是一个命名的locale对象,你就可以使用locale::global来设置这个全局的locale。随后创建的所有流都使用这个全局的locale。
?你可以使用imbue成员函数来显式地给一个流设置一个locale。
当你写的软件使用locale时,为用户可见的数据使用本地化的格式。也就是说,你需要使用用户熟悉的格式显示数字,实例化一个locale,然后用这个实例来调用imbue成员函数填充这个流从而给用户正确地显示数字。但是如果你正在把数据写到一个文件中或者其他中间顺序化存储介质中,为了可移植性,请使用“C”locale。如果你的代码显式地改变了全局的locale,然后你需要显式地使用“C”来显式地调用imbue成员函数来填充你的文件流。你可以有两种方法来这样做,通过使用名字“C”来创建一个locale,或者调用locale::classic()函数。如下所示:
ofstream out("data.dat");
out.imbue(locale::classic());
out << pi << endl; // Write using C locale
读数字是相同的。例如,在法语中读数字并且使用“C”locale写数字,如下所示:
double d;
cin.imbue(locale("french"));
cin >> d;
cout << "In English: " << d;
如果你运行这个程序并且输入300,00,它将打印出300。
为了使得一个流遵循一个locale的数字传统,使用这个目标的locale对象来显式地调用imbue成员函数来填充这个流。或者,如果你希望所有创建的流使用某个特定的locale,可以把它作为全局locale安装它。货币处理起来稍有不同;关于如何读写货币,请参考13.4节中的例子。
请参考13.4节。






