Life, Education, Death

プログラミング以外でも思ったことをつらつらと書きたい

Win32APIなんて使いたくないでもUTF-8で出力したい

以前書いたときにはロケールの設定によりUTF-8のデータが読み込めるようになった

しかし!!!

まだ出力ができていなかった・・・

Win32APIなら

WideCharToMultiByteMultiByteToWideCharなどがあり、確かにこれでも出来るっぽいのだけれども
環境依存があまりよろしくなかったので今回はboostを使った。

Boostを使う

ここで詳しく書いてあった。
やり方はlocaleにUTF-8に変換してくれるfacetを追加するようだ。
いまいち、このlocaleとfacetの関係とか意味がMSDNのドキュメントを見ていてもわからなかった。

そもそも、全然ロケールに関するいい情報源が見つからなくて困った。
このエントリーは古いBoostのようで今のバージョンではうまくいかないので、boost/detail/utf8_codecvt_facet.hppのヘッダーファイルを見て、設定を行う。

#define BOOST_UTF8_BEGIN_NAMESPACE namespace hoge{
#define BOOST_UTF8_DECL
#define BOOST_UTF8_END_NAMESPACE }
#include <libs/detail/utf8_codecvt_facet.cpp>
#undef BOOST_UTF8_DECL
#undef BOOST_UTF8_BEGIN_NAMESPACE
#undef BOOST_UTF8_END_NAMESPACE


と定義とインクルードを書く。BOOST_UTF8_BEGIN_NAMESPACEには任意のnamespaceを設定してくれということのようだ。他はよくわかってない。

std::locale loc = std::locale();
loc = std::locale(loc, new hoge::utf8_codecvt_facet );
std::locale::global(loc);


今回は全体のロケールを変更する必要があったので、locale::globalメソッドを利用した。
ちなみに、setlocaleではC++の方のロケールは変更されないがこれなら全体を変更できるとのこと。

locale::globalは実行後の全てのオブジェクトに影響を与える。
coutやcinなどのオブジェクトはシュワルツカウンタを使って、インスタンスを先に作っているから影響がないので

std::locale loc = std::locale();
loc = std::locale(loc, new LogCollector::utf8_codecvt_facet );
std::locale::global(loc);
cout.imbue(loc);


のようにしなければいけない。

最後に

http://d.hatena.ne.jp/Cryolite/20041027も結構参考になった。
ロケールに関してよくわからないことだらけなことが、とてもわかった。もっとみんな文字コードをちゃんとしたいって思わないのだろうか・・・
Webなら簡単なのになーと思う。

ちなみに文字コード変換はICUを使うと簡単にいける。

訂正

utf8_codecvt_facetをnewするところのnamespaceが間違っていたのを修正(2009/04/30)。