文字コード

絶対に手を触れたくない世界、文字コードShift_JISEUCUTF-8UTF-16くらい知ってればええねん。


wchar_tを使ってみよう→wchar用関数で日本語を出力しようとするものの何も出てこない→あああああ
他に日本語のファイル名のファイルを作ろうとすると死ぬ、など。
結論から言うと、C/C++標準ライブラリのwchar_t用関数・クラスにはwchar_tをどのように扱うかという設定をlocaleから得ます。manによると「ロケール (locale) は言語や文化ルールの集合」らしいです。標準では"C"となっています。これは簡単に言えば文字についての扱いは「(元)1バイト文字」のみとなります。日本語を出力しようとすると不正な文字と認識され死亡します。
Cでは

setlocale(int category, const char *locale)

C++では

std::locale::global(const std::locale &)

を使用します。ヘッダやこれ自体のwchar_t版は自分で調べてください。カテゴリはLC_ナントカというのがいくつもあり、LC_ALLで全て一度に設定します。std::localeのコンストラクタではデフォルト引数になってるっぽい。多分ここではLC_CTYPE(character type)が相当します。で、文字列でロケールを設定する訳ですが、"C"で問題のデフォルト。""で今の環境のデフォルトとなります。"japanese"ってしてる例もよく見ますが、その国のPCでその国の文字が出る""でいいのではないでしょうか。


さすがにこれは実験が必要だろうと思い、試してみました。VC2008。

#include <iostream>
#include <fstream>

int main()
{
	//std::locale::global(std::locale(""));
	//std::wcout.imbue(std::locale(""));
	std::wcout << L"あいうえお" << std::endl;
	std::wofstream ofs(L"あ.txt");
	ofs << L"あいうえお" << std::endl;
	return 0;
}

なんでmainがwmainじゃないんだとか細かいことを気にしてはいけません。結果は、一番上の行が無いと

  • コンソールに何も表示されない。
  • ファイル(あ.txt)は作成される
  • ファイルに書き込めていない(0バイト)

globalで設定しても、cout系などのすでに作成されているオブジェクトには適用されないのでwcout.imbue(std::locale(""))しろなどの話がありましたが、VC2008ではstd::locale::globalだけで全て大丈夫でした。が、imbueの名前は覚えておいた方がいいでしょう。VC2005はある意味黒歴史かも。デフォルトの文字コード設定がマルチバイトからUNICODEに変わった時だしなあ。日本語ファイル名が死んだ報告も2005だったような。


とりあえず言っておくと、wchar_tを使うとぶっちゃけcharを使ったコードより環境依存性が上がるでしょう。多分。残念ながら。そもそもVCはsizeof(wchar_t)=2・UTF-16gccではsizeof(wchar_t)=4・UCS4(コンパイラオプションで2バイトに変更は可能)らしいです。


私の意見ではこの辺りの話は偉い人に任せておくのが一番いいと思うのですが、この辺りの整備はまだまだと思ってcharってた方がいいんでしょうか。つまり、JavaC#は神。ほんとにそう思えてくるから困る。