打开主菜单

<locale>w:C++標準程式庫中的一個w:头文件,定义了w:C++标准中的w:区域设置(本地化策略集)的类及相关的模板类实例。[參 1]

C++ locale对象与C语言locale机制只有一点链接:如果一个具名的C++ locale被设为global locale,则C语言的global locale也被改动。

目录

相关环境变量编辑

  • LC_COLLATE 字符排序规则
  • LC_CTYPE 字符分类,字符编码,字符是单字节还是多字节
  • LC_MONETARY 货币格式
  • LC_NUMERIC 数字格式
  • LC_TIME 日期时间格式
  • LC_MESSAGES 提示信息的语言
  • LC_ALL 以上所有
  • LANG 除以上其它任何用途

C/C++程序的默认区域设置是C或POSIX,二者基本是一码事。

locale类编辑

C++标准定义的locale类,作为本地化的策略集:

namespace std {
class locale {
public:
  // types:
    class facet;
    class id;
    typedef int category; //provides bitmask values to denote standard facet families.
    static const category // values assigned here are for exposition only
        none = 0,
        collate = LC_COLLATE, 
        ctype = LC_CTYPE,
        monetary = LC_MONETARY, 
        numeric = LC_NUMERIC,
        time = LC_TIME, 
        messages = LC_MESSAGES,
        all = collate | ctype | monetary | numeric | time | messages; // =LC_ALL
// construct/copy/destroy:
    locale() throw();                                                 //11 复制当前的全局C locale
    locale(const locale& other) throw();                              //22 Copy constructor
    explicit locale(const char* std_name);                            //33  Constructs a copy of the named C library locale。如Linux下中文系统的locale名字为"zh_CN.UTF-8",Windows下中文系统为"chs"
    locale(const locale& other, const char* _Locname, category _cat);      //44 复制other,并使用以_Locname命名的locale中由_cat指定的facet
    template <class Facet> locale(const locale& other, Facet* _f);     //55 从当前全局locale复制构造一个locale对象,并使用由_f指定的facet。编程者一般派生定义一个Facet,然后通过这个ctor使用它
    locale(const locale& _loc, const locale& _other, category _cat);         //66 复制other,并使用_other所指的locale中由_cat指定的facet
    ~locale() throw(); // non-virtual
    const locale& operator=(const locale& other) throw();             //77 Assignment operator
    template <class Facet> locale combine(const locale& other) const; 
          // 返回一个新的locale对象,复制了当前locale对象,使用other对应的locale中的Facet替换

// locale operations:
    basic_string<char> name() const; //获得当前locale的完整名称
    bool operator==(const locale& other) const;
    bool operator!=(const locale& other) const;
    template <class charT, class Traits, class Allocator>
        bool operator()(const basic_string<charT,Traits,Allocator>& s1, //使用当前locale的collate facet来比较两个字符串;用于std::sort等算法
                                   const basic_string<charT,Traits,Allocator>& s2) const;
// global locale objects:
    static locale global(const locale&); //把参数locale设置为全局locale
    static const locale& classic();//获得缺省的“C”locale
};
}

流在初始化时使用全局的locale。此后,可以通过流的imbue()成员函数改用别的locale。如果流已经初始化完毕,这时再修改全局locale,对这个流没有影响。

facet编辑

facet翻译为策略或“刻面”,原意指钻石切削加工后的一个小面。locale 是多种 facet对象的容器(实际上在locale类对象中用一个std::vector存储该locale类支持的所有facet的指针)。一个程序可以有多个locale对象,从而避免了C语言程序只能使用一个global locale的问题。多个locale对象可以共用同一个facet对象实例。因此facet对象内部使用w:引用计数。而locale::facet作为基类,主要定义了一些机制,以支持内部的引用计数器。如w:Visual C++的类库中locale::facet定义了_Incref、_Decref成员函数。此外locale::facet的拷贝构造和拷贝赋值函数声明为private或使用=delete,禁止外界拷贝facet,也禁止外界对facet赋值。从locale::facet派生出一大批模板类,各自管理某一种功能;按照类别(category)列出如下:

  • locale::ctype 类别
    • ctype // 字符分类和转换
    • codecvt // 字符编码转换
  • locale::collate 类别
    • collate // 字符比较
  • locale::message 类别
    • messages // 从信息目录中获得本地化信息
  • locale::numeric 类别
    • numpunct // 有关数字和布尔运算表达式中标点符号及格式信息
    • num_get // 代表数字或布尔值的字符串的解析
    • num_put // 代表数字或布尔值的格式化字符串的生成
  • locale::monetary 类别
    • moneypunct // 货币表达式中的标点符号及格式
    • money_get // 代表货币值的字符串的解析
    • money_put // 代表货币值的格式化字符串的生成
  • locale::time 类别
    • time_get // 代表日期和时间的字符串的解析
    • time_put // 代表日期和时间的格式化字符串的生成

因为C++的locale所有的成员函数均声明为const,所以一旦定义locale实例就不能修改。可以用自定义的facet去改造一个已有的locale产生新locale,然后植入(imbue)IO流中:

std::locale new_loc(old_loc, new NewFacet);
locale new1_loc("zh-CN", new_loc, locale::ctype); //使用new_loc的ctype

基类facet有一个public static member,型别为locale::id,名字为id。从facet类派生的各个模板类,使用这个基类facet的数据成员id,作为各个派生模板类实例化后的类标识。id初值为0,在运行时第一次访问类标识id时确定其非0值随后即保持不再改变。id用来“以某个facet型别为索引,搜索locale之内的一个facet”。例如:

locale loc_ger( "German_Germany" ); 
locale loc_chs("chs");
typedef  ctype<wchar_t> ctype_wchar; // 定义一个类型
const ctype_wchar &cw_ger =use_facet<ctype_wchar>(loc_ger); //一个对象
const ctype_wchar &cw_chs =use_facet<ctype_wchar>(loc_chs); //另一个对象
(cw_ger.id == cw_chs.id) // 为真
(use_facet<codecvt<wchar_t,char, mbstate_t>>(loc_ger).id == use_facet<codecvt<wchar_t,char, mbstate_t>>(loc_chs).id) // 为真

每个基于facet派生的模板类的实例,可以有多个不同的对象,如上例中的ctype<wchar_t>类型有表示德文字符集的对象cw_ger、表示简体中文字符集的对象cw_chs。 每个locale对象,都有一个内部存储facet指针的数组,数组索引值为facet的id,数组的元素为facet对象的地址,该facet对象的类标识id等于数组索引值。数组元素也可为空指针。在构造一个locale对象时,可以替换默认使用的facet对象或者增加一个facet对象,这是通过查找locale对象的内部的facet指针的数组,把facet对象的类标识id作为数组索引值,在相应数组元素位置上写入facet对象的地址。

每个基于facet派生的模板类,所有公开的成员函数均声明为const;所有的public成员函数均不是虚函数,而是将调用操作委托给一个protectedw:虚函数,后者的命名类似于对应的public函数,只不过在前面加上do_,如numpunct::truename会调用numpunct::do_truename()。因此,如想改变某个facet,应该在该facet为基类写一个派生类,并覆盖重写其对应的protected函数。

ctype编辑

ctype是一个预定义的facet类模板,用于分类字符、大写小写转换,在内部字符与locale使用字符之间转换。例如,所有基于std::basic_istream<charT>的流输入使用当前流所使用(imbue)的locale的std::ctype<charT>过滤出输入字符中的空白符。标准输出流使用std::ctype<charT>::widen()来把窄字符(narrow-character)扩展为w:宽字符

标准库提供两个单独的(locale独立的)模板特化:

  • std::ctype<char>提供最小的"C" locale窄字符分类的等价实现。该特化版本使用查表方法实现字符分类。
  • std::ctype<wchar_t>提供本地字符集的w:宽字符分类。

此外,C++程序中创建的每个locale实现自己的(locale-specific)ctype特化版本。

模板类的成员类型char_type,其内容是模板参数 CharT。

类成员函数is用于给一个字符或者字符串中每个字符做分类,如字母数字alnum, 字母alpha, 控制字符cntrl, 数字digit, 字母数字或标点字符graph, 小写字母lower, 可打印字符print, 标点punct, 空白符space, 大写字符upper, 16进制数字xdigit。

类成员函数narrow与类成员widen用于宽字符、窄字符的转换,不适用于多字节编码的东亚字符。

类成员函数tolower与类成员toupper用于字符或字符串的大小写的转换。

类成员函数scan_is与类成员scan_not用于扫描字符串,返回第一个属于(或不属于)某类别的字符。

collate编辑

collate是一个预定义的facet类模板,用于w:字符串的词典序比较、多字节表示单个字符的成组(grouping)。类方法compare比较一个locale下的两个字符串。类方法transform用于转换locale下的字符串到一个string对象,这个string对象也是用于词典序比较,这适用于一个字符串与很多个字符串比较的情形。类方法hash用于根据一个字符串计算出hash值。

codecvt编辑

codecvt是一个预定义的facet类模板,用于w:字符串的编码转换:

templat <class InnerCoding, class ExternCoding, class State> class std::codecvt: public locale, public codecvt_base{};

codecvt类模板的第一个模板参数是内部字符类型;第二个模板参数是外部字符类型,如w:简体中文w:Windows操作系统默认使用的gbk(代码页936)w:Linux使用的w:utf-8;第三个模板参数是字符转换所需的中间状态的类型,如std::mbstate_t。C++标准允许两种codecvt类模板的实例化:

  1. 内部字符类型与外部字符类型都是char。这种情况实际上没有做字符转码。
  2. 内部字符类型与外部字符类型分别是w:宽字符(wchar_t)与窄字符(char)。窄字符既包括单字节字符集,也包括中日韩等的双字节字符集、多字节字符集。

从一个locale通过use_facet()函数获得codecvt facet,则该codecvt的外部字符类型就是该locale的ctype所指定的char的字符集类型。从而获得了在wchar_t与该locale使用的char的字符集之间来回转码的能力。编程时,如果需要在两种窄字符(如w:gbkw:big5)之间转码,可以分别获得w:gbkw:big5的locale,然后使用两个locale各自的codecvt facet,以w:宽字符(wchar_t)为中介实现转码。

codecvt的类方法in()是从外部编码转换到内部编码;类方法out()是从内部编码转换到外部编码。 把宽字符串转换为东亚的几种多字节字符集编码的字符串,需要特别注意预留出足够的目标字符串的长度;另外,目标字符串的所有字节在转换前应用数值0做初始化,转换后目标字符串自然成了以0结尾,就无需用其他方法去判断目标字符串转换后的长度。codecvt的类方法length可用于判断在给定外部字符串、给定内部字符串的字符长度上限,返回多少个外部字符可以被转换。

basic_filebuf模板类内部使用了通过imbue()函数指定的locale的codecvt做字符编码转换,因此文件流fstream可以用于字符转码。而basic_stringbuf模板类不使用codecvt,因此不能用于字符转码。默认情况,文件流输出与输入的硬盘文件总是用窄字符(char)编码的。即Wide file I/O的两个对象wofstream与wifstream,分别默认把宽字符转码为窄字符后写入硬盘文件、从硬盘文件读取窄字符后转码为宽字符。

如果需要把宽字符通过wofstream写入硬盘文件,一种解决办法是写一个codecvt<wchar,char,mbstate_t>的派生类,内部实际上不做任何转码。把此codecvt派生类加入新的locale中,然后imbue()到wofstream中。

numpunct编辑

处理数值I/O中相关的标点符号。成员方法包括:

  • decimal_point(); //返回一个字符用来表示小数点
  • thousands_sep(); //返回一个字符用来表示千分位符号
  • grouping(); //返回一个string表示数值的分组规则
  • truename(); //true的文本表示
  • falsename();//false的文本表示

C++标准规定,numpunct<char>numpunct<wchar_t>必须作为模板特化实现。

num_put编辑

处理数值的格式化输出。put成员函数把值val写入指定的w:输出流迭代器

num_get编辑

处理数值输入的解析。get成员函数解析w:输入流迭代器first和last之间的字符序列,得出一个对应类型的数值。确切格式由ios_base型的格式化标志确定。

time_get编辑

处理时间日期输入的解析。成员方法包括:

  • dateorder date_order();//返回facet中年月日的次序
  • iter_type get_time();//解析输入流的字符串表示的时间,和strftime的%X格式一致
  • iter_type get_date();//解析输入流的字符串表示的日期,和strftime的%x格式一致
  • iter_type get_weekday() ;//解析输入流的字符串表示的星期的日期
  • iter_type get_month();//解析输入流的字符串表示的月份
  • iter_type get_year();//解析输入流的字符串表示的年份

time_put编辑

处理时间日期的格式化输出。

字符串与流转换的类模板编辑

wstring_convert编辑

C++11引入的3个std::codecvt的派生类。从C++17过时。在宽字符与字节字符串之间转换。都是独立的编码转换facet Codecvt,不是由locale管理的facet Codecvt。

  • std::codecvt_utf8 用于在UTF-8编码的字节流与宽字符串之间的转换;
  • std::codecvt_utf16 用于在UTF-16编码的字节流与宽字符串之间的转换。UTF-16编码的字节流是大端序还是小端序由参数指定。
  • std::codecvt_utf8_utf16 用于在UTF-8编码的字节流与UTF-16字符串之间的转换。

wbuffer_convert编辑

C++11引入,从C++17过时。在宽字符流缓冲区字符与字节流缓冲区之间转换

模板函数has_facet编辑

测试一个locale是否实现了指定facet

模板函数use_facet编辑

访问一个locale对象中的某个facet:

namespace std {
    template <class facet>
       facet const&  use_facet(locale const& loc);
};

这个模板函数从一个locale类中取得指定的facet类的只读引用,然后就可以调用该facet类提供的工作函数。该模板的类型参数可以区分属于一个locale类的不同的facet类。

字符分类与处理的函数编辑

  • isalnum
  • isalpha
  • iscntrl
  • isdigit
  • isgraph 字母数字或标点字符
  • islower
  • isprint
  • ispunct
  • isspace
  • isupper
  • isxdigit
  • tolower
  • toupper

参考文献编辑

  1. Langer, Angelika,Klaus Kreft[2000].Standard C++ IOStreams and Locales.Addison Wesley Longman, Inc.ISBN 9780321585585