Win32APIなんて使いたくないでもUTF-8で出力したい
以前書いたときにはロケールの設定によりUTF-8のデータが読み込めるようになった
しかし!!!
まだ出力ができていなかった・・・
Win32APIなら
WideCharToMultiByteやMultiByteToWideCharなどがあり、確かにこれでも出来るっぽいのだけれども
環境依存があまりよろしくなかったので今回は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なら簡単なのになーと思う。
訂正
utf8_codecvt_facetをnewするところのnamespaceが間違っていたのを修正(2009/04/30)。