読者です 読者をやめる 読者になる 読者になる

VC++でUnicode文字の出力を試みる→MessageBoxでの出力はできたけど標準出力の方法分からず

C++

このエントリーをはてなブックマークに追加

前回の記事に引き続いて、VC++で日本語文字を扱う方法についてあれこれ調べている。調べれば調べるほど、開いてはいけない蓋を開いているような気がしてならない。


今回はUnicode文字をVC++で出力することを目指した。結論から言うと、MessageBoxを使ってポップアップを開く方法ではUnicode文字を出力できたけど、標準出力で出力する方法は最後まで分からなかった。実験環境はWindows 7 + VS 2013 Express。

Unicode文字列を用意

はてなに貼り付けると一部の文字が数値文字参照になってしまったので、ideoneを参照してください

最初に、Shift_JISでは表せないけどUnicodeでは表せる文字列を探す。有名なのは「𠮟」という字。これは「叱」と似て非なる字。詳しくは新常用漢字が引き起こす文字コード問題 - 新常用漢字表が迫るUnicode移行、「シフトJIS」では対応不可能:ITproを参照。他にも、「〓」(ウムラウト文字)とか、怪しい顔文字「(*´ڡ`●)」「( ・ิϖ・ิ)」はShift_JISで表せない。

今回はこれらをすべてぶちこんで以下の文字列を作った。

wchar_t uni[] = L"Unicodeのテストです。𠮟〓(*´ڡ`●)( ・ิϖ・ิ) 正しく表示されるでしょうか";

このようにShift_JISで表せない文字がソースコードに入った場合、ソースコード文字コードを変更する旨勧められるはず。指示にしたがってUTF-8 (コードページ65001)で保存した。

MessageBoxで表示→成功

この文字列をMessageBoxで表示させる。ワイド文字列を表示させたいのでMessageBoxW()を使う。

    MessageBoxW(NULL, uni, uni, MB_OK);

すると、文字化けせず正しくポップアップされた。

標準出力→失敗

次に標準出力。普通にwcoutで出力。

    wcout << uni << endl;

しかし、結果は以下の通り。途中の文字までしか表示されない。

これは多分、コマンドプロンプトShift_JISしか表示できないのが原因。なんとかできないかと思って色々調べたのだが、結局Unicodeの文字を表示できるようにする方法はわからなかった。以下は調べたことの一部。

chcpを使う

chcpコマンドを使うとUTF-8の文字が表示されるという情報があった。C++プログラムからchcp相当のことを行うにはSetConsoleOutputCP(65001);とすればよいという。しかしフォントがなくて表示されなかった。。 参照記事にはレジストリをいじる旨書かれていたがそこまでは深入りしていない。もしかしたらSetCurrentConsoleFontEx()関数を使えばフォント設定できるかもしれないけど試していない。

_setmode()を使う

意味もわからずc++ - how to properly print utf8 characters in windows console? - Stack Overflowを真似して_O_U16TEXTとか_setmode()とかいうのを使ってみた。Shift_JISで表現可能な文字のみ表示できたが、他はNG。

localeを設定する

setlocaleでUnicodeを使うように設定すればいいんじゃないの?と思い、setlocale(LC_ALL, "Japanese_Japan.65001"); とやってみたがダメ。少しでも関係のありそうなもの、例えば #pragma setlocale(".UTF-8") とか #pragma execution_character_set("utf-8") とかを訳も分からず書いてみたが、どれもうまくいかなかった。

exeを実行して > out.txtする

ダメ。コンソールと結果同じ。

まとめ

VC++Unicodeは鬼門。

今回の実験で使用したソースコード

http://ideone.com/iJCMSx:ideoneに置いた。
一部問題があるが、はてなにも貼り付ける。

#include <iostream>
#include <clocale>
#include <iostream>
#include <Windows.h> // for MessageBoxW

using namespace std;

int main(int argc, char* argv[])
{
    wchar_t uni[] = L"Unicodeのテストです。&#134047;〓(*´&#1697;`●)( ・&#3636;&#982;・&#3636;) 正しく表示されるでしょうか";
    wcout << uni << endl;
    MessageBoxW(NULL, uni, uni, MB_OK);

    return 0;
}

参考