VSでもUTF-8な文字列を入出力したい
まずそもそもVSのstd::wcout
の挙動を整理します。
std::wcout.imbue(std::locale(""));
std::wcout
<< L"𠮷野家" << std::endl
<< L"sekai" << std::endl;
このコードはなにも出力しません。
この場合コマンドプロンプトの文字コードがcp932なのでUTF16->cp932の変換をコマンドプロンプトが行います。が、一度でも変換できない文字(ex. 𠮷
)に当たると以降一切の変換を行いません。
この場合UnicodeなんだからUTF16->UTF-8の変換をコマンドプロンプトがやってくれることを期待したいところですが、してくれるけど表示するフォントがない、という状況になります。 フォントの変更にはレジストリの変更が必要らしいのですが、私は一回もうまくやれた試しがありません。 そもそもユーザーにそんな手間を要求するのは本末転倒です。
ConEmuからcmd.exeを呼んで見ましたが
フォントを認識してくれないようです。
Windowsで使える他のコンソールとしてnyagosがあります。これをConEmuから使ってみます
この場合コマンドプロンプトの文字コードがcp932なのでUTF16->cp932の変換をコマンドプロンプトが行います。変換できない文字はスキップして変換をする仕様のようです。
やはりUTF16->UTF-8の変換はされてもフォントがうまく認識してくれないようです。
UTF8->UTF16への変換をします。
Windows環境ではstd::coutが直接ロケール依存の文字コードなので、Linuxとコードの共通化ができません。 そこでWindows環境、といってもmingwのstd::wcoutがぶっ壊れているのはさんざん述べたとおりなので、実質VS環境のみですが、std::coutにはUTF-8の文字列を流し込むことにして、それをUTF16に変換してstd::wcoutにバイパスします。ただそれだけです。 単純な変換しかしないので上記の問題はそっくりそのまま残ります。が、コードの共通化のために有用と考えます。
端末の文字コードはUTF-8と仮定します。EUC-JPの可能性は低いと考えます。
この場合std::cout
はUTF-8な文字列を流せばいいのでとくにすることはありません。
端末にbash.exe+mintty.exeなどのcmd.exeに依存しない何かを使うことを前提にします。
この文字コードはUTF-8と仮定します。
この場合std::cout
はUTF-8な文字列を流せばいいのでとくにすることはありません。
cmd.exe上で動かすことを仮定します。先の過程より、chcp932な環境に出力することになります。 std::cout -> u8ostreambuf -> std::wcout -> chcp932への変換(cmd.exe) -> 出力 という流れになります。
つまり
#ifdef _MSC_VER
std::wcout.imbue(std::locale(""));
u8ostreambuf buf1{ std::wcout.rdbuf() };
u8istreambuf buf2{ std::wcin.rdbuf() };
auto tmp1 = std::cout.rdbuf(&buf1);
auto tmp2 = std::cin.rdbuf(&buf2);
#endif
のようにどこかに書いておいて、
std::cout << u8"ありきたりな世界" << std::endl;
のように使えます。