检测当前的语言环境是否使用了 UTF-8 编码(三篇文章:先用setlocale()设置编码,再用nl_langinfo()进行检测。locale对象可以使用langLocale.name() == "zh_CN"判断)

时间:2024-11-20 09:37:32

C/C++程序中,locale(即系统区域设置,即国家或地区设置)将决定程序所使用的当前语言编码、日期格式、数字格式及其它与区域有关的设置,locale设置的正确与否将影响到程序中字符串处理(wchar_t如何输出、strftime()的格式等)。因此,对于每一个程序,都应该慎重处理locale设置。

C locale和C++ locale是独立的。C locale用setlocale(LC_CTYPE, “”)初始化,
C++ locale用std::locale::global(std::locale(“”))初始化。这样就可以根据当前运行环境正确设置locale。

根据环境变量中设置编码信息

使用第二个参数为”"的setlocale()调用会自动根据LC_ALL、LC_CTYPE和LANG环境变量的值设置合适的值。

  1. #include <locale.h>
  2. #include <locale>
  3. using std::locale;
  4. int main()
  5. {
  6. setlocale(LC_CTYPE, "");
  7. // 使用当前locale,但numpunct使用缺省的,因此不会在输出数字时加上千位分隔符
  8. locale::global(locale("").combine<std::numpunct<char> >(locale::classic()));setlocale()的有效的locale串
setlocale()的有效的locale串

在windows下可以用”.codepage”如”.936″来指定,linux下可以指定得更详细:”zh_CN.GB18030″。zh(语言,这里指中文)_CN(地区,这里指*).GB18030(该语言所使用的字符集)。可能有一个以上的国家/地区说某种特定的语言,例如,巴西和葡萄牙都说葡萄牙语。反之,一个国家/地区可能有一种以上的官方语言。例如,加拿大有两种官方语言:英语和法语。语言和地区两个因素组合才会确定一个字符集,即使用某个语言的某一地区会创建一个该地区的字符集来表示该语言的字符。

windows下不能直接指定“GB18030”,可能只能用“GBK”。

检测当前的语言环境是否使用了 UTF-8 编码
  • 先用setlocale()设置编码,再用nl_langinfo()进行检测

为检测当前语言环境是否使用了 UTF-8 编码。首先必须调用 setlocale(LC_CTYPE, “”) 函数,依据环境变量设置语言环境。nl_langinfo(CODESET) 函数也是由 locale charmap 命令调用,从而查找当前语言环境指定的编码名称。

  1. BOOL utf8_mode = FALSE;
  2. if(!strcmp(nl_langinfo(CODESET), "UTF-8")
  3. utf8_mode = TRUE;•直接查询环境变量
  4. 这项测试假设 UTF-8 语言环境名称中有值“UTF-8”,但实际情况并不总是如此,所以应该使用 nl_langinfo() 方法。
  5. char *s;
  6. BOOL utf8_mode = FALSE;
  7. if ((s = getenv("LC_ALL")) || (s = getenv("LC_CTYPE")) || (s = getenv ("LANG")))
  8. {
  9. if (strstr(s, "UTF-8"))
  10. utf8_mode = TRUE;
  11. }

附加:

区域设置(即locale)就成为语言、国家/地区和代码页的唯一组合。可以通过调用setlocale 函数在运行时更改区域设置和代码页设置。

参考:

locale C++  百度

C++之国际化(2) --- locale

分类: C++ 2014-03-03 13:30 487人阅读 评论(0) 收藏 举报

转载自:http://hi.baidu.com/nicker2010/item/8cc13bce77f11b3899b498eb

解决国际化问题,通常是通过locale环境,它被用来封装国家(地域)和文化之间的转换行为。
一个locale就是一个参数和函数的集合。根据X/Open公约,环境变量LANG用来确定当时的locale
不同的浮点数、日期、货币格式等则根据这个locale确定。

确定一个locale,需要采用以下字符串格式:
language[_area[.code]]
language表示语言,例如英语或者德语
_area表示该语言所处的地域、国家或者文化。用它可以在相同语言的不同国家之间实行转换
code定义字符编码方案,其重要性主要体现在亚洲,因为在那相同的字符集有不同的编码方案,如汉字的BIG5和GB
下面列出了典型的语言名称:

c           Default: ANSI-C conventions (English, 7 bit)
de_DE       German in Germany
de_DE.      88591   German in Germany with ISO Latin-1 encoding
de_AT       German in Austria
de_CH       German in Switzerland
en_US       English in the United States
en_GB       English in Great Britain
en_AU       English in Australia
en_CA       English in Canada
fr_FR       French in France
fr_CH       French in Switzerland
fr_CA       French in Canada
ja_JP.jis   Japanese in Japan with Japanese Industrial Standard (JIT) encoding
ja_JP.sjis  Japanese in Japan with Shift JIS encoding
ja_JP.ujis  Japanese in Japan with UNIXized JIS encoding
ja_JP.EUC   Japanese in Japan with Extended UNIX Code encoding
ko_KR       Korean in Korea
zh_CN       Chinese in China
zh_TW       Chinese in *
lt_LN.bit7  ISO Latin, 7 bit
lt_LN.bit8  ISO Latin, 8 bit
POSIX       POSIX conventions (English, 7 bit)

但它们尚未标准化。
对程序而言,这些名称是否标准化无关紧要,
因为locale的信息是由使用者根据以某种形式提供的。
普遍的做法是:程序只需读取环境变量或者类似的数据库,判断可用的locales,
然后便可以将“选择正确locale名称"的任务交给使用者。

C程序可以使用函数setlocale()来设定一个locale。
改变locale会对isupper()和toupper()之类的字符函数以及printf()之类的I/O函数产生影响
但C的解决方案有很多的限制。
C++的标准中,locale则被泛化,设计得更有弹性

locale使用示例:
cin.imbue(locale::classic()); ///使用经典的C的locale从标准流中读取数据
cout.imbue(local("de_DE")); ///imbue函数用来安装locale,///德国的小数点为逗号(',')
double value;
while(cin>>value)
    cout<<value<<endl;

其中cin.imbue(locale::classic());相当于cin.imbue(local("C"));

一般来说,除非需要读取某个固定格式来读取数据,否则程序不会预先定义一个特别的locale
而是会利用环境变量LANG来确定相应的locale
另外一种可能是读取一个locale名称然后利用之

示例代码:
locale langLocale(""); ///从环境变量LANG中读取缺省的locale对象
cout.imbue(langLocale);
bool isChina;
///locale.name()获取locale名称
if(langLocale.name() == "zh_CN" || langLocale.name() == "zh_TW")
    isChina = true;
else
    isChina = false;
if(isChina)
    cout<<"输入locale名称:"<<endl;
else
    cout<<"Input the name of locale:";
string locString;
cin>>locString;
if(!cin)
{
    if(isChina)
        cerr<<"读取数据时候发生错误!";
    else
        cerr<<"Error While reading";
    return;
}
local cinLocale(locString.c_str());

cin.imbue(cinLocale);
string value;
while(cin>>value)
    cout<<value<<endl;

locale类的静态函数global()可以用来安装一个全局的locale对象
这个对象可以用来作为某函数的locale对象参数的缺省参数
如果global()设定的locale对象是有名称的,则相当于C的locale调用了std::setlocale(LC_ALL,"")

不过,设定全局的locale对象,并不会替换已经存储于对象内部的locale。
它只能改变由缺省构造函数所产生的locale对象
例如下面的就是stream安装缺省的locale对象
cin.imbue(locale());
cout.imbue(locale());
cerr.imbue(locale());

下面是CodeBlocks中locale声明:

class locale
  {
  public:
    typedef int category;

class facet;
    class id;
    class _Impl;

friend class facet;
    friend class _Impl;

/**has_facet和use_facet是两个重要的友元函数

has_facet确定locale中是否有类型为_Facet的facet

use_facet返回locale中使用的类型为_Facet的facet对象的引用

*/

template<typename _Facet>
    friend bool has_facet(const locale&) throw();

template<typename _Facet>
    friend const _Facet& use_facet(const locale&);

template<typename _Cache> friend struct __use_cache;

/**locale中使用的facet*/

static const category none  = 0;
    static const category ctype  = 1L << 0;
    static const category numeric = 1L << 1;
    static const category collate = 1L << 2;
    static const category time  = 1L << 3;
    static const category monetary = 1L << 4;
    static const category messages = 1L << 5;
    static const category all  = (ctype | numeric | collate |
        time  | monetary | messages);

/**不同类型的构造函数以及析构*/
    locale() throw();
    locale(const locale& __other) throw();
    explicit locale(const char* __s); ///locale名为__s

//产生__base的一个副本,类型__cat中所有的facet将被__add的facet替换

locale(const locale& __base, const locale& __add, category __cat);

///相当于locale(__base, locale(__s),__cat);
    locale(const locale& __base, const char* __s, category __cat);

///产生__other的一个副本,并安装__f所指的facet

template<typename _Facet> locale(const locale& __other, _Facet* __f);
    ~locale() throw();

const locale& operator=(const locale& __other) throw();

///产生this的一个副本,并将__other中型别为_Facet的facet装入
    template<typename _Facet>locale combine(const locale& __other) const;
    string name() const; ///locale的名称字符串

/**比较函数*/
    bool operator==(const locale& __other) const throw ();
    inline bool operator!=(const locale& __other) const throw ()
    { return !(this->operator==(__other));  }

///operator()使得我们可以运用locale对象作为字符串比较工具,运用collate facet,(STL仿函数的行为)

////使用示例:vetor<string> vec;   std::sort(vec.begin(),vec.end(),locale("zh_CN"));
    template<typename _Char, typename _Traits, typename _Alloc>
    bool operator()(const basic_string<_Char, _Traits, _Alloc>& __s1,
                    const basic_string<_Char, _Traits, _Alloc>& __s2) const;

/**两个重要的成员函数global, classic

global将参数安装为全局的locale,并返回上一个全局locale

classic()返回locale("C")

*/
    static locale global(const locale&);
    static const locale& classic();

......

};

C++ 标准库的 locale 类用法

分类: 转帖好文 2011-01-04 23:22 6375人阅读 评论(1) 收藏 举报

目录(?)[+]

原来一篇总结了下 C 标准库的 setlocale() 用法,这篇讲解的是 C++ 标准库中 locale 类的用法。

参考:

locale 类在头文件 <locale> 中声明,另外可能会用到 <stdexcept> 中的标准异常类,和<iostream> 中的流对象类。

GNU libstdc++ 中的 locale

参考:

  1. locale 对象的构造

    构造函数有以下几个常用的重载形式:

    • locale()

      构造一个 locale 对象,它和程序当前的全局 locale 属性相同。

    • explicit locale(const char* _Locname)

      根据 locale 名构造一个 locale 对象,影响该对象的所有的 locale 类别(category),locale 名就是 C 标准库 setlocale() 中使用的名字,所以 libstdc++ 用的是 POSIX 标准的 locale 名,而 Windows CRT 实现的 locale 类,用的是 Windows 的 setlocale() 中特有的 locale 名字。

      和 setlocale() 类似,如果要使用环境设置的 locale 时,可以将 locale 名字写为空字符串""。

      注意这个构造函数被声明为显式的(explicit),说明隐式的参数转换会被认为是语法错误,例如:locale loc = "zh_CN.UTF-8" 会在编译时报错。

      当指定的 locale 名为 NULL,或是一个无效的名字,此构造函数会抛出 runtime_error 异常。

    • locale(const locale& _Loc, const locale& _Other, category _Cat)
      locale(const locale& _Loc, const char* _Locname, category _Cat)

      首先用 _Loc 拷贝构造一个 locale 对象作为基础 locale,然后从 _Other 或 _Locname 指定的 locale 中,抽取里面由 _Cat 指定的 category,替换掉基础 locale 中相应的 category。

      category 是 int 的 typedef,在 locale 类内有一些预定义的 category 常量(类静态常量成员),分别对应于 C 标准库中预定义的 locale 类别常量:

      locale::category 常量与 setlocale() 中的 category 参数对应关系
      locale::category 常量 setlocale() 中的 category
      all LC_ALL
      collate LC_COLLATE
      ctype LC_CTYPE
      messages LC_MESSAGES
      monetary LC_MONETARY
      none 表示所有 category 的空集(LC_ALL 的补集)
      numeric LC_NUMERIC
      time LC_TIME

      category 类型被用作掩码类型,所以上面 locale 类别可以用 | 进行组合,比如:monetary | time。

  2. locale 类静态方法

    • const locale& classic()

      得到一个表示 C locale 的 locale 对象。

    • locale global(const locale& _Loc)

      设置程序全局 locale 为 _Loc,返回以前的全局 locale。

      所谓全局 locale,就是流对象不用 imbue() 方法采用指定 locale 时的缺省 locale,相当于 C 标准库中 setlocale() 设定的 locale。程序初始化时,全局 locale 为 C locale。

      例如,设定全局 locale 为环境设置的 locale:

      1 // 相当于 setlocale(LC_ALL, "");
      2 locale::global(locale(""));
  3. locale 对象方法

    • string name() const

      返回 locale 的名字,例如,打印当前全局 locale 名字可以为:

      1 locale lc;
      2 cout << "Current global locale is: " << lc.name() << endl;

      注意:name() 返回的是 string 对象,而 wostream 的 << 操作在 string 上是无定义的,所以这个会出错:wcout << lc.name(),除非自己重载 wostream 的 << 操作。

  4. locale 使用示例与问题

    locale 类的作用和 setlocale() 相同:一是输出 wchar_t 字符时,根据活动 locale 将字符从 UCS 编码转换为 Native ANSI 编码,最后传递给终端、控制台设备;另一个作用是使用特定 locale 的某些 category 做操作(相应的函数、方法称为 locale 相关方法),比如:时间、货币文本的格式化,排序等等。

    使用 wcout 流对象和 locale 对象,输出 wchar_t 型字符限制很大,以下是正确向终端输出中文的例子:

    01 const wchar_t* strzh = L"中文abc";
    02  
    03 try
    04 {
    05     locale lc("zh_CN.UTF-8");
    06     locale::global(lc);
    07     // wcout.imbue(lc); // libstdc++ 的实现 imbue() 不起作用
    08     wcout << L"Zhong text is: " << strzh << endl;
    09 }
    10 catch (runtime_error& e)
    11 {
    12     cerr << "Error: " << e.what() << endl;
    13     cerr << "Type:" << typeid(e).name() << endl;
    14 }

    上面程序的运行环境为:终端和 shell 都使用 zh_CN.UTF-8 编码,最后用重定向输出到文件的方法测试出:输出的文本为 UTF-8 编码。

    限制来自于:无法混用 char 和 wchar_t 版的流对象,而很多对象只有返回 string 的方法(比如:locale::name()),导致必须使用 char 版的 cout、cerr 等,或者可以用 string::c_str() 方法。

    GNU libstdc++ 中实现的 locale 类,在使用时注意以下问题(VC8 中没有下面前两个问题):

    • 流对象的 imbue() 方法不起作用

      使用 locale::global() 设定全局 locale 后,wostream 可以输出正确转换的 Native ANSI 编码字符。而使用 wostream::imbue() 后,流对象无法向终端传递正确转换的编码字符。在测试 wcout 时,使用重定向输出到文件的方法发现:wcout 在输出含有 ASCII 外的 wchar_t 时,无法正确转换到 Native ANSI 编码字符,而转换成字节 0x3F(对应 ASCII 字符 ?)。

    • char 和 wchar_t 版的流对象不能混用

      和 glibc 实现的 C 标准库类似,在 libstdc++ 中混用 char 和 wchar_t 版的流对象,也会出现意外的错误,比如下面代码:

      01 const wchar_t* strzh = L"中文abc";
      02 locale lc("zh_CN.UTF-8");
      03 locale::global(lc);
      04  
      05 // 这句会引发后面的 wcout 输出错误
      06 cout << "abc" << endl;
      07  
      08 // 即使刷缓冲区和清空流对象状态也无法解决问题
      09 cout.flush();
      10 cout.clear();
      11 wcout.flush();
      12 wcout.clear();
      13  
      14 wcout << L"Zhong text is: " << strzh << endl;

      程序的输出如下:

      # ./test_03.exe | hd
      00000000 61 62 63 0a 5a 68 6f 6e 67 20 74 65 78 74 20 69 |abc.Zhong text i|
      00000010 73 3a 20 2d 87 61 62 63 0a |s: -.abc.|
      00000019

      上面程序错误之处为:没有将 "中文" 的 UCS-4 LE 编码 2D 4E 00 00 87 65 00 00 转换为正确的 UTF-8 编码 E4 B8 AD E6 96 87,而是将每个 wchar_t 字符的最低字节(L'中' 为 2D,L'文' 为 87)传给了终端。

      如果把 cout << "abc" << endl; 这句放到 wcout 之后,那么 wcout 是可以正常输出中文的,但 cout 就工作不正常了,根本就不向终端传递字符。

    • Cygwin 的 libstdc++ 中的 locale 实现

      测试版本:g++ 4.3.4,bash 3.2.49,cygwin1.dll 1.7.1,libstdc++6 4.3.4-3

      构造 locale 对象时,C.UTF-8,zh_CN.UTF-8,en_US.UTF-8 这些 locale 名字都无效,都会抛出runtime_error 异常,就连使用环境 locale 的空字符串参数 "" 都无效。只有一个 locale 名是有效的,就是最小的 C locale,即程序初始化时的默认locale。另外,Cygwin bash 启动后默认的 locale 为 C.UTF-8,可用查看 LANG 环境变量得知。

    • cout、wcout 与 printf()、wprintf() 的区别

      我的感觉是 cout、wcout 没有 printf()、wprintf() 好用,至少 printf()、wprintf() 都可以输出 wchar_t 型字符,而 cout 默认无法输出 wchar_t 型字符(当然可以自己重载 <<),而把 wchar_t 当整数输出,把 wchar_t[] 和 wchar_t* 输出个首地址。

      wostream 是可以以 char 型字符、字符串为 << 的参数的,但如果传入的 char 型字符在 ASCII 范围之外(在 0x00 ~ 0x7F 之外),wostream 是不会将正确的 Native ANSI 编码序列传递给终端、控制台设备的,导致终端中不会显示超出 ASCII 之外的字符。

      string 没有在 wostream 的 << 上定义,但可以用 string::c_str() 的方法让其输出 char 型字符,前提是事先知道 string 对象中不含有超出 ASCII 之外的字符。

Windows CRT 中的 locale

参考:

我在 VC8 下测试,和 GNU libstdc++ 的差异挺大,表现结果是:wcout.imbue(lc) 起作用,可以在控制台中输出含中文的 wchar_t 型字符,并且控制台得到的是根据 locale 转换的 Native ANSI 编码。Windows 的 locale 类实现不支持 UTF-8(代码页 65001),所以要用 GBK 的 locale,对应的 locale 名为 chs,长名字:Chinese_People's *.936。

用 VC8 的 locale::global(lc) 时有个奇怪的现象,它导致后面的 wcout 流输出到控制台完全没有显示,我将输出重定向到文件,发现结果正常,和用 imbue() 输出的相同,为 GBK 编码的中文。所以我认为,无法显示中文字符是 Windows 的控制台设备实现,以及 CRT 和控制台设备交互的问题,CRT 根据 locale 转换编码的工作是正常的。

另外,VC8 的 cout 和 wcout 的混用,以及 printf() 和 wprintf() 混用,都没有 GNU 实现的问题。

==================

https://blog.****.net/haiross/article/details/45074355