localeの基本
日付の表示方法は国によって違う。アメリカなら’Friday, July 23, 2021’みたいになり、日本なら’2021/7/23’みたいになる。ほかにも、数字のカンマやピリオドのつけ方や金額表示の仕方もちがう。このような違いを扱うのがlocaleという概念。
1 2 3 4 |
char *setlocale( int category, const char *locale ); |
catergoryには時刻を扱う。LC_TIMEなどいろいろあるが、ここでは一括で扱うLC_ALLを基本的に考える。
locale文字列は国・地域・言語を表す文字列で、”en-US”などと指定するがプログラム開始時には”C”になっており、”C”は1文字1charであることを意味している。”en-US”などの場合は言語と地域を表しており、さらに追加して”en-US.UTF8″などとも書ける。localeにNULLを渡すと現在の値を取得する。localeに空文字を渡すとユーザのデフォルトの国や地域そしてコードページが指定される。
1 2 |
printf("Current locale is '%s'\n", setlocale(LC_ALL, NULL)); printf("Locale for empty is '%s'\b", setlocale(LC_ALL, "")); |
これを英語のWindowsで実行すると以下のように出力される。
1 2 |
Current locale is 'C' Locale for empty is 'English_United States.1252' |
Englishが言語で、United Statesが国・地域、1252がコードページを表しこれは’en-US.1252″と同じ意味だろう。
localeに”.UTF8″とするとコードページだけ変えることができる。ここで指定したコードページはmbtowcsなどでワイド文字列に変換するときに使われる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
wchar_t szW[32]; { setlocale(LC_ALL, ".1252"); char p[] = "あ"; // 文字化け、コードページ1252(latin1)に'あ'はない // ここの"あ"はソースコードの文字コードがどうであろうとANSIと解釈されるはず // つまり英語環境のWindowsではpはそもそも文字化けしている。 mbtowc(szW, p, strlen(p)); } { setlocale(LC_ALL, ".UTF8"); char p[] = u8"あ"; // pはUTF8 // ちゃんと変換できる。 mbtowc(szW, p, strlen(p)); } |
C++の場合
上記の話はC言語の話だった。C言語ではlocaleは1つしかなく、グローバル設定があるだけだったが、C++ではlocaleはクラスになった。
C言語との関係
stdにもstd::setlocale()があり、これは上記と同じようにグローバルなlocaleを設定するものだろう。
localeクラス
- C++ではcategoryの代わりにFacetと言うようになった。
- C言語ではsetlocal()で設定した情報はグローバルでどこかに格納されそれがアプリコードからは見えないところで参照されるが、C++のストリームでは明示的にlocaleクラスを設定しなければならない
コンストラクタ―
引数のないコンストラクタstd::locale()は、グローバルlocaleクラスのコピーを作成する。
文字列を指定
std::locale("ja-JP")
は”ja-JP”のlocaleクラスを作成する。作成しただけではどこにも反映されない。
std::locale::classic();
これはsetlocal(LC_ALL, "C")
と同じlocaleのインスタンス参照をかえす。
グローバルに設定
staticなstd::locale::global(locale& l)
を呼ぶと、locale lをグローバルに設定する。
ストリームに設定
C++方式でグローバルに設定したlocaleはC言語の関数には影響を与えるがストリームには影響しない。ストリームに反映させるにはimbue()
を使って設定する。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
{ std::locale loc("en-US"); std::wcout.imbue(loc); std::wcout << loc.name().c_str() << " " << d << '\n'; } { std::locale loc("ja-JP"); std::wcout.imbue(loc); std::wcout << loc.name().c_str() << " " << d << '\n'; } { std::locale loc("de-DE"); std::wcout.imbue(loc); std::wcout << loc.name().c_str() << " " << d << '\n'; } |
1 2 3 |
en-US 1,234.56 ja-JP 1,234.56 de-DE 1.234,56 |
Facetとstd::codecvt
省略、facetはbasic_stringのtraitsのように自分で作ることができる、codecvtもfacetの一つ、これは非推奨でなおかつlocaleを使いにくい。globalなlocaleも反映されない。