前几日在看一个关于多线程下locale问题:在多线程下locale not independent问题。无意中在网上搜到一篇讲解C locale 和C++ locale的文章,觉得很好,链接如下:
http://stdcxx.apache.org/doc/stdlibug/24-3.html
今天尝试翻译一下,加深理解。翻译如下:
Apache C++ 标准库用户向导
24.3 C Locale和C++ Locales区别
正如我们目前所看到的,C Locale和C++ Locales提供了类似服务。然而,C++ Locale所想表达的语义信息却不同于C Locale。
为了进一步细节地探索他们的不同,我们来看看locales是如何被使用的。
24.3.1 C locale的一般使用
c locale 通常会被用作默认locale (defaule locale),本地locale (native locale),或者是多locale应用(multiple locale applications)中。
Default locale:作为开发,如果你从不涉及到互联网功能的开发需求,那么你也不会调用std::setlocale()。如果你认为你用户总是在标准的US English ASCII下使用你的应用,那么你也没有localization的需求。如果你连locale都不知道的话,那么你一定总是在默认的locale下,也就是在US English ASCII locale下开发你的应用。
Native locale:如果你确实有计划在你的程序里支持本地化,最合适的策略就是在程序一开始就获取本地local信息,然后不要再更改设置。这样你的应用会适配的选择某个特定locale,并贯穿到应用的整个运行过程中。如此用户在启动应用之前就可以显示的设置他们喜欢的locale。在UNIX系统中,他们通过设置环境变量,例如LANG来设置locale;其它操作系统有其它的方法去设置locale。
你在程序里可以通过调用set::setlocale("")以及传一个空字符串作为locale的名字来指定一个用户有偏好的locale设置。空的字符串是告诉setlocale去使用用户在环境中指定的locale作为应用的默认locale。
Multiple locales:有时候你不得不工作在多locales的情形下。例如,一个应用是给瑞士人使用的,那么你期望输出的消息能支持意大利语,法语和德语。因为C locale是一个全局的(global)的数据结构,所以你必须在不同locales之间多次切换去支持不同语言。
我们来看一个在多locales情形下工作的例子。想象一下你的应用需要打印费用清单给全世界的顾客。那个费用清单一定是符合顾客本地语言的,所以你的应用就必须根据不同的语言打印不同语言费用清单。其中费用清单里的价格是一个独立的价格列表。假设我们的应用被一个美国公司使用,价格列表是US English。
应用在US English下读价格列表(输入),但是写费用清单(输出)是支持客户本地语言的,比如说德语。因为C有仅有一个全局的(global) locale,全局的locale会同时影响输入和输出,所以全局的locale需要在输入和输出之间进行切换。在价格从英文价格列表中读出来之前,locale必须先从德文locale切换成US English locale。在价格加入到费用清单之前,全局的locale必须再切换成德文locale。为了从价格列表里读下一个价格,locale又要切换为English,如此反复。 图6解释了这个过程。
Figure 6 : Multiple locales in C
这是刚才例子的C代码:
double price;
char buf[SZ];
while ( ... ) // processing the German invoice
{
std::setlocale(LC_ALL, "En_US");
std::fscanf(priceFile, "%lf", &price);
// convert $ to DM according to the current exchange rate
std::setlocale(LC_ALL, "De_DE");
std::strfmon(buf,SZ, "%n", price);
std::fprintf(invoiceFile, "%s", buf);
}
使用C++ locale对象可以极具地简化这个过程。The iostreams in the C++ Standard Library are internationalized so that streams can be imbued with separate locale objects. C++标准库下的iostreams是国际通用的(internationalized),所以streams能够贯穿独立的locale对象。例如,输入流是English locale对象,输出流是German locale对象。如此,locales切换就没有必要了,正如图7所显示的:
Figure 7: Multiple locales in C++
这是C++代码的例子:
priceFile.imbue(std::locale("En_US"));
invoiceFile.imbue(std::locale("De_DE"));
moneytype price;
while ( ... ) // processing the German invoice
{
priceFile >> price;
// convert $ to DM according to the current exchange rate
invoiceFile << price;
}
假设这个例子已经有
货币值的例子相对简单,在locales之间切换不是很麻烦。然而,一旦涉及到代码层面切换,这个问题就变的主要起来了。
为了更好的理解这一点,让我们再看一下图2中的JIS encoding scheme使用shift语法如何在图8中变大方便起来的。正如你说记得那样,当你解析字符句子的时候,你必须要维护一个shift状态。
Figure 8: Figure 2中日文文本在JIS中的编码
假设你在解析一个含有在JIS下编码的文本的多字节文件,正如你在图9中所看到的。当你解析这个文件,你不得不跟踪shift状态以至于你可以知道如何翻译你所读到的字符节,如何以合适的宽字节来表达这些字符节。
Figure 9:使用全局C locale解析多字节文本输入
在解析文本过程中,全局C locale可以来回切换;例如,文本输入的locale状态从JIS 编码切换到EUC编码。每当locale切换到新的状态,当前的locale状态就无效了,在你的应用里你不得不小心维护由locale切换带来的状态切换。
当local切换变得通用的时候,这个问题就迎刃而解了。然而,在多线程环境下,全局C locale会带来严重的问题,因为一个线程下的locale状态不经意间会被另外一个线程下的locale给更改了。因为这个原因,在多线程环境下通用化C程序就很困难。
如果你使用C++ locales,另一方面这个问题也很容易解决了。你可以每个数据流绑定到一个独立的locale对象,这样问题就不会出现locale被不期望修改的问题。让我们来看一个C++ locales的例子。
priceFile.imbue(std::locale("En_US"));
invoiceFile.imbue(std::locale("De_DE"));
moneytype price;
while ( ... ) // processing the German invoice
{
priceFile >> price;
// convert $ to DM according to the current exchange rate
invoiceFile << price;
}
24.3.2 C++ locales的一般使用
C++ locale通常被用作支持多locales的默认的locale;和全局locale。
Classic locale. 如果你涉及的程序并不需要支持本地化工作,那么使用C++ locales和使用C locales没什么区别。 如果你总是认为你的应用程序的用户总是在US English ASCII环境下,那么你也不需要请求本地化。对于你,C++标准库提供了一个预定义的locale对象std::locale::classic(),它正是US English ASCII locale。
Native locale. Native locale是受用户和系统管理员喜欢的一个locale。在UNIX系统里,通常通过设置环境变量例如LANG来完成。你也可以通过调用构造函数std::locale("")为native locale创建一个C++ locale对象,也就是通过请求一个以空字符串作为输入创建的locale。这个空字符串告诉系统从环境中得到locale的名字,这等同于C库函数的std::setlocale("")。
Named locales. 正如上面所说,一个local可以有一个名字。标准的locale的名字是“c”。不幸的是,其他locales的名字平台依赖。查询你的系统文档来获知你的系统是什么locales被安装以及locale是如何命名的。如果你尝试用一个无效的名字去创建一个locale对象,构造函数会抛出一个runtime_error异常。
Multiple locales. 当你使用C++ locales,工作在不同的locales就变得很容易了。你在C下面所做的locales切换,在C++里就不再有必要了。你可以绑定每个流到不同的locale 对象。你也可以传递locales对象到不同地方使用,这完全没有问题。
Global locale. 正如C,C++也有全局的locale。 一开始,全局locale是如前所说的标准locale。你也可以通过调用std::locale::global()去改变全局locale。
你也可以通过调用默认的构造函数std::locale::locale(),为当前的全局locale创建一个快照。这些快照也是locales object独立的,不受接下来的全局locale改变的影响。
通用化组件像iostreams,使用的全局locale作为默认的locale。如果你不显式的把一个流和一个特定的locale绑定在一起,那么这个流对象就默认和一个在它被创建时的那个全局的locale绑定。
C下全局locale所能做的,C++全局locale也能做到。在程序启动一旦开始,程序就激活了本地locale。换言之,本地locale被激活作为global locale,并作为locale快照,之后所有的工作都依赖这个locale。以下的代码描述了这个过程:
std::locale::global(std::locale("")); //1
...
std::string t = print_date(today, std::locale()); //2
...
std::locale::global(std::locale("Fr_CH")); //3
...
std::cout << something; //4
//1 设置本地locale作为全局locale
//2 总是使用全局locale快照作为你当前使用的locale对象。假设函数print_date()是格式化日期的。为了格式化日期,你将要提供一个全局locale的快照给函数print_date()。
//3 切换全局locale到French全局locale
//4 注意在这个例子中,标准流cout仍然是跟classic locale(标准的locale)绑定的,因为在程序启动的时候,std::cout就被创建出来了。改变全局locale不会影响先前已经存在的流的locales。如果你想让一个新的全局locale绑定到cout上,你应该在调用完std::locale::global()之后调用stf::cout.imbue(locale())。
24.3.3 C locale和C++ locales之间的联系
C locale和C++ locale是完全独立的。然而,C++ locale对象是有个名字的,通过std::locale::global()使locale对象变成全局locale会引起C locale改变,这个改变会通过调用std::setlocale()。当它发生时,在C++程序里,locale敏感的C函数会使用变化了的C locale。
在一个C程序里,是没有办法改变C++ locale的。
另外有需要云服务器可以了解下创新互联scvps.cn,海内外云服务器15元起步,三天无理由+7*72小时售后在线,公司持有idc许可证,提供“云服务器、裸金属服务器、高防服务器、香港服务器、美国服务器、虚拟主机、免备案服务器”等云主机租用服务以及企业上云的综合解决方案,具有“安全稳定、简单易用、服务可用性高、性价比高”等特点与优势,专为企业上云打造定制,能够满足用户丰富、多元化的应用场景需求。