boost 的 locale

前面讨论 boost.date_time 的时候就碰到过 locale 这个东西,其基本的做法就是为某些事务提供相应的 facet,这些 facet 可以挂载到 locale 对象里面,这样输出的时候将 locale 通过 imbue 嵌入到 std::ostream 里面,这时 ostream 就会利用提供的 locale 进行输出。另一方面也可以直接修改全局的 locale 对象,这样 ostream 等就能自动的用上对应的 scale。但是 STL 的 scale 存在一些问题:

  • 设定全局的 locale 后可能存在副作用,典型的一种是某些数字输出时小数点变成逗号,这很可能导致 CSV 这类文件输出格式出错;
  • 对 utf-8 的支持等

boost.locale 其实是源自 CppCMS 项目,那里典型的一个需求是处理多国语言需要不同的 locale。那么 boost.locale 究竟提供了一些什么特性?

  • 一个主要的是使用了 ICU,这使得我们可以操纵 unicode 和 utf-8 互相转换。
  • 另一个是提供了与 GNU gettext 的整合,这使得我们如果希望实现 i18n 变得格外的简单。
  • 能够将不同编码的字符进行转换
  • 简单的字词、句子的切分

嗯,这么多功能似乎难以在一两个例子里面说清楚,我们这里针对 facet 本身做一些简单的探讨。标准库中的 locale 有如下一些 facet,这些也都在 boost.locale 中给出了对应的实现,所有的 facet 实现必须继承 std::locale::facet 这个类:

  • codecvt 实现了编码转换,提供了 in/out 进行相关的处理;
  • collate 实现了字符串的比较等一些相关的功能,boost 甚至提供了几种不同的比较方式(忽略大小写,无视 accent 等),除了 compare,另外提供了 transform 与 hash
  • ctype 进行字符的分类和转换
  • messages 负责从 message catalog (这是啥?)中读取字符串的
  • moneypunct 和货币相关的一些东西,高阶一点 facet 包括 money_get 和 money_put(boost 提供了一些 as 系的 IO manipulator,比如 as::currency 就能很容易利用这两个 facet 输入输出货币)
  • 对应的有 numpunct 和高阶版本 num_put 和 num_get;
  • time_get 与 time_put
  • 某些还提供了 _byname 的版本,这些允许使用 locale 名字初始化对应的 facet

OK,我们可以看看几个简单的例子。首先我们知道 locale 返回的结果一般是这样的,

$ locale
LANG="en_US.utf-8"
LC_COLLATE="en_US.utf-8"
LC_CTYPE="en_US.utf-8"
LC_MESSAGES="en_US.utf-8"
LC_MONETARY="en_US.utf-8"
LC_NUMERIC="en_US.utf-8"
LC_TIME="en_US.utf-8"
LC_ALL=

这里面特定的一些东西正好对应了 STL 的 locale 提供给我们的 facet,因此我们的程序如果希望使用这些信息,可以用 cstdlib 的 getenv 获得对应的值,这样我们的程序就会 respect 这些 settings。

#include <boost/locale.hpp>
#include <locale>
#include <cstdlib>
#include <ctime>

int
main (int, char**) {
  time_t now = time (0) ;

  {
    const std::locale& g = std::locale::classic () ;
    std::cout << "locale name: " << g.name () << std::endl
              << "currency: " << boost::locale::as::currency
              << 1.1 << std::endl
              << "date: " << boost::locale::as::date
              << now << std::endl ;
  }
  std::cout << "--------------------------\n" ;

  boost::locale::generator gen ;
  {
    const char* lang = getenv ("LANG") ;
    std::locale g = gen(lang) ;
    std::cout.imbue (g) ;
    std::cout << "locale name: " << g.name () << std::endl
              << "LANG = " << lang << std::endl
              << "currency: " << boost::locale::as::currency
              << 1.1 << std::endl
              << "date: " << boost::locale::as::datetime
              << now << std::endl ;
  }
  std::cout << "--------------------------\n" ;

  {
    std::locale g = gen("zh_CN.utf-8") ;
    std::cout.imbue (g) ;
    std::cout << "locale name: " << g.name () << std::endl
              << "currency: " << boost::locale::as::currency
              << 1.1 << std::endl
              << "date: " << boost::locale::as::datetime
              << now << std::endl ;
  }
  std::cout << "--------------------------\n" ;

  return 0 ;
}

程序需要跟 libboost_locale 连接,结果大致如下

locale name: C
currency: 1.1
date: 1335025594
--------------------------
locale name: *
LANG = en_US.utf-8
currency: $1.10
date: Apr 22, 2012 12:26:34 AM
--------------------------
locale name: *
currency: ¥1.10
date: 2012-4-22 上午12:26:34
--------------------------

看样子以后应该多用 locale 搞输入输出了。同时编码之类的东西也会轻松很多了。

——————
Is any thing too hard for the LORD? At the time appointed I will return to you, according to the time of life, and Sarah shall have a son.

Advertisements
boost 的 locale

发表评论

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / 更改 )

Twitter picture

You are commenting using your Twitter account. Log Out / 更改 )

Facebook photo

You are commenting using your Facebook account. Log Out / 更改 )

Google+ photo

You are commenting using your Google+ account. Log Out / 更改 )

Connecting to %s