C++/Locale
<locale> 是C++標準程式庫中的一個頭文件,定義了C++標準中的區域設置(本地化策略集)的類及相關的模板類實例。[參 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,對這個流沒有影響。
中文locale的名字:
- linux下: zh_CN.utf8,zh_CN.GBK zh_CN.GB2312 zh_CN.GB18030
- windows下:
- 標準格式的locale:"Chinese (Simplified)_People's Republic of China.936"、Chinese_China.936、zh-CN.936 、 Chinese (Simplified)_China.936、zh-CN.utf8
- 非標準格式的locale:chs、 Chinese-simplified 、 Chinese、 ZHI 、cp936、gbk、zh-Hans、zh、zh-CN、"chinese"、"chinese-simplified"、"chinese_CHN"
- 不能使用的locale: Chinese.936,chs.936,Chinese.GB2312,chs.GB18030
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類模板,用於字符串的編碼轉換:
templat <class InnerCoding, class ExternCoding, class State> class std::codecvt: public locale, public codecvt_base{…};
codecvt類模板的第一個模板參數是內部字符類型;第二個模板參數是外部字符類型,如簡體中文Windows操作系統默認使用的gbk(代碼頁936)|gbk(代碼頁936)或Linux使用的utf-8;第三個模板參數是字符轉換所需的中間狀態的類型,如std::mbstate_t。C++標準允許兩種codecvt類模板的實例化:
- 內部字符類型與外部字符類型都是char。這種情況實際上沒有做字符轉碼。
- 內部字符類型與外部字符類型分別是寬字符(wchar_t或char16_t或char32_t)與窄字符(char或char8_t)。窄字符既包括單字節字符集,也包括中日韓等的雙字節字符集、多字節字符集。
從一個locale通過use_facet()函數獲得codecvt facet,則該codecvt的外部字符類型就是該locale的ctype所指定的char的字符集類型。從而獲得了在wchar_t與該locale使用的char的字符集之間來迴轉碼的能力。編程時,如果需要在兩種窄字符(如w:gbk與w:big5)之間轉碼,可以分別獲得w:gbk與w: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中。
類模板codecvt_byname是codecvt的派生類:
template< class InternT, class ExternT, class State >
class codecvt_byname : public std::codecvt<InternT, ExternT, State>;
其中InternT應是寬字符類,ExternT是窄字符類。構造函數的參數指明所用locale的名稱。如GBK在linux下的locale名是"zh_CN.GBK",而windows下是".936"或"gbk"或"chs",因此做跨平台的話仍然要給不同的系統做適配。
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
[編輯]std::wstring_convert 是 C++11 提供的用於字符編碼轉換的工具類,能夠方便地在寬字符類型(std::wstring)和多字節字符串類型(std::string)之間進行轉換,尤其適用於 UTF-8 和寬字符之間的轉換。然而,隨着 C++17 的推出,std::wstring_convert 已被標記為廢棄。C++17 推薦使用更現代的字符編碼轉換庫,如 std::format、std::filesystem 等,或者通過第三方庫(例如 iconv、Boost.Locale 或 UTF8-CPP)進行編碼轉換。
類模板std::wstring_convert在std::string與寬字符串std::basic_string<Elem>(其中Elem是寬字符類型)之間轉換。使用的編碼轉換facet——Codecvt,必須由 std::wstring_convert所有,不能屬於某個locale。構造函數需要傳入一個codecvt對象的指針,如果沒有傳入,則默認使用new codecvt來創建。std::wstring_convert自行維護codecvt對象的生命周期,它的析構函數會調用delete操作符來刪除該對象。這就限制了只能使用通過new操作符來創建的codecvt,而不能使用從std::locale中獲取的codecvt。 標準預定義的codecvt有3個:
- std::codecvt_utf8 註:在utf8和UCS2/UCS4之間轉換
- std::codecvt_utf16 註:在 UTF-16 和寬字符類型之間轉換
- std::codecvt_utf8_utf16 註:在utf8和utf16之間轉換
獲取其它字符編碼的codecvt,需要使用std::codecvt_byname,這個類可以通過字符編碼的名稱來創建一個codecvt。由於歷史原因,std::codecvt_byname的析構函數是protected的,std::wstring_convert不能對它調用delete,所以首先要自行定義一個類來繼承std::codecvt_byname。
通常的定義類型:
template< class Codecvt, //字符转换facet
class Elem = wchar_t> //宽字符类型
class wstring_convert;
主要成員函數:
- from_bytes 從字節字符串到寬字符串
- to_bytes 從寬字符串到字節字符串
示例:
#include "stdafx.h"
#include <iostream>
#include <string>
#include <codecvt>
using namespace std;
using WCHAR_GBK = codecvt_byname<wchar_t, char, mbstate_t>;
using WCHAR_UTF8 = codecvt_utf8<wchar_t>;
// linux下为"zh_CN.GBK"或“zh_CN.gb2312”或"zh_CN.gb18030" Windows下为 .936或chs或gbk
#define GBK_NAME ".936"
int main()
{
// 定义一个utf8字符串
string result = u8"国人";
// gbk与unicode之间的转换器
wstring_convert<WCHAR_GBK> cvtGBK(new WCHAR_GBK(GBK_NAME));
// utf8与unicode之间的转换器
wstring_convert<WCHAR_UTF8> cvtUTF8;
// 从utf8转换为unicode
wstring ustr = cvtUTF8.from_bytes(result);
// 从unicode转换为gbk
string str = cvtGBK.to_bytes(ustr);
cout << str << endl;
getchar();
return 0;
}
wbuffer_convert
[編輯]C++11引入,從C++17過時。在寬字符流緩衝區字符與字節流緩衝區之間轉換
#include <iostream>
#include <sstream>
#include <locale>
#include <codecvt>
int main()
{
// wrap a UTF-8 string stream in a UCS4 wbuffer_convert
std::stringbuf utf8buf(u8"z\u00df\u6c34\U0001f34c"); // or u8"zß水🍌"
// or "\x7a\xc3\x9f\xe6\xb0\xb4\xf0\x9f\x8d\x8c";
std::wbuffer_convert<std::codecvt_utf8<wchar_t>> conv_in(&utf8buf);//构造一个wbuffer_convert,指定codecvt,指定字节流utf8buf
std::wistream ucsbuf(&conv_in);
std::cout << "Reading from a UTF-8 stringbuf via wbuffer_convert:\n";
for(wchar_t c; ucsbuf.get(c); )
std::cout << std::hex << std::showbase << c << '\n';
}
模板函數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
參考文獻
[編輯]頁面Template:ReflistH/styles.css沒有內容。
- ↑ Langer, Angelika; Klaus Kreft. Standard C++ IOStreams and Locales. Addison Wesley Longman, Inc. [2000]. ISBN 9780321585585.