12.3.1  类locale概述

12.3.1locale概述

类locale实现一种类型安全的、多态的“刻面”集合,可使用刻面类型检索。换言之,一种刻面具有双重角色:某种意义上,刻面正好是类的接口,同时也是locale刻面集合的索引。通过两个函数模板:use_facet< >和has_facet< >访问locale类型刻面。

978-7-111-51399-5-Chapter12-8.jpg

978-7-111-51399-5-Chapter12-9.jpg

在调用use_facet<Facet>(loc)时,类型参数选择一种刻面,要使所有名称类型的成员均有效。在locale中,若facet(刻面)不存在,该刻面会抛出标准异常bad_cast。一个C++程序可以检查出是否类locale使用函数模板has_facet<Facet>实现一个特殊的刻面。类locale对象中,用户定义的刻面可能被装备并使用,就像许多标准刻面一样。

类库中还提供了一种成员操作符模板operator()(basic_string<C,T,A>&,basic_string<C,T,A>&)。对于标准集合和校核字符串,此类模板适用于使用类locale的对象作为一个可预测参数。常规的全局接口界面主要用于传统的ctype函数,类似于isdigit()和isspace()。尤其对于给定的locale对象,一个C++程序可以调用其成员函数issapce(c,loc)。这使升级现存的抽出符变得很容易了。

一旦一种刻面(或其引用)从一个locale对象获取,即可通过调用use_facet< >,才能使用该刻面的功能。既然一些locale对象涉及了刻面,其成员函数的结果会被期望,并可重复使用。

在连续调用一个locale类的facet成员函数的过程中,输入/输出流的插入器或抽出器或流缓冲区成员函数,其返回的结果应该是一致的。类locale对象在构建时,其名称字符串可能是(“POSIX”);或两个被命名的locale对象,拥有同一个名称,其他是不可能的。类lo-cale的名称可能用于“相等”比较,无命名的locale仅能等于它本身(即其备份)。对于一个无命名的locale对象,成员函数name()返回类型为字符串类型。

对于实现编程的国际化,仅仅翻译“文字所带信息”通常是不够的。数值、货币、日期……均具有不同的各种不同的规格,都是需要必须遵守的。之外,用于操作字母的函数,应根据字符进行编码,以确保正确处理特定语言中所有字母的字符。根据POSIX和X/Open标准,C程序可使用函数设定一个locale的各种属性。改变locale会对isupper()和toupper()之类的字符分类/操作函数以及printf()之类的I/O函数产生影响。

然而C的解决方案毕竟包括了诸多限制。由于locale是全局属性,同时使用多个locale是比较麻烦的。locale不能扩展,仅能提供“由编译器选择供应”的设施。若某个必须遵守的国家协议未被C locale支持,则只有改弦更张。程序员不可能为支持某种特殊文化而定义新的locales类。

C++标准程序库利用面向对象方式解决了上述问题。首先,locale相关的细节被封装在型别为locale的对象中。这样在同一时刻运用多个locale即可解决诸多问题。与locale相关联的各种操作,会运用相应的locale对象。将每一个locale对象关联至到每个流中,流的各成员函数会利用该对象迎合相应规格。经典的C locale可以直接赋值到标准输入通道中。所谓经典的C locale,即使用最初的C形式来进行格式化数字、日期、字符分类等工作。

std::locale::classic()用于获取对应的经典locale对象。

表达式std::locale(“C”)和std::locale::classic()的效果相同。

该表达式根据给定的名字产生一个locale对象。“C”是特殊名称,实际上是各个C++实作版本唯一必须支持的名称。C++标准并未强制要求支持其他locale,通常各个C++实作版本都会支持其他locales。表达式cout.imbue(locale(“de_DE”))可以实现将名称为de_DE的locale赋值到标准输出通道中。唯有当系统支持该locale,该动作才会成功。若所希望构造的locale名字不能被C++实作版本识别出来,将会抛出一个runtime_error异常。

若一切顺利,输入时将按照经典C规格,输出时会按照德国规格。这样就可以按英文格式读取浮点数(德文是使用逗号作为小数点的)。

通常,除非需要按照某个固定格式来读写数据,否则程序不会预先定义一个特定的类locale。而利用环境变量LANG确定相应的locale。另一种可能是读取一个locale名称时,需要运用它。

例12-1

978-7-111-51399-5-Chapter12-10.jpg

例12-1的执行结果为:

978-7-111-51399-5-Chapter12-11.jpg

如果程序遵循各地的习惯,应该使用相应的locale对象。类locale的静态成员global()可以安装一个全局的locale对象。该对象可用来作为某函数的locale对象参数的默认参数。如果global()函数所设定的locale对象是有名称的,而C的locale相关函数也会做出相应的响应;若该对象没有名称,则C函数的执行结果由实作版本决定。

对于后继被执行的C函数会做出相应的注册操作,即那些C函数所受到的影响和以下调用操作所带来的影响相同。

978-7-111-51399-5-Chapter12-12.jpg

通过设定全局locale,不能替换已储存于对象内的locales。只能改变由默认构造函数所产生的locale对象。stream对象存储的locale对象不会被locale::global()替换。若希望已经存在的stream使用某个特定的locale,必须使用imbue()函数通知该流(stream)。

当默认构造一个locale对象时,全局locale就会发挥作用。产生新的locale即是全局lo-cale的副本。下述代码是3个标准的streams安装默认的locale类对象的实例。

978-7-111-51399-5-Chapter12-13.jpg

在C++STL中使用locale,读者牢记C++ locale机制和C locale机制之间仅有松散的耦合关系。若一个具名的C++ locale对象被设为全局locale,则全局C locale会很被动。通常,不能假设C和C++函数所操作的locales保持一致。