setlocale テスト プログラム
コマンドライン引数に CATEGORY[=LOCALE]
を並べて動作を調べる簡単なプログラムです。
CATEGORY
の "LC_" は省略可能で、大文字・小文字は区別しません。
引数が CATEGORY
では
setlocale(CATEGORY, NULL)
の結果を表示します。CATEGORY=LOCALE
の形式では上記に加えて
setlocale(CATEGORY, LOCALE)
の結果も表示します。
オプション -l
は使用可能なカテゴリ表を出力します。
#include <stdio.h>
#include <locale.h>
#include <string.h>
#include <map>
#include <string>
#if _MSC_VER
#define strcasecmp _stricmp
#endif
using namespace std;
struct StrCaseCmp
{
bool operator ()(const string& a, const string& b) const
{ return strcasecmp(a.c_str(), b.c_str()) < 0; }
};
typedef map<string, int, StrCaseCmp> LC_CATEGORY;
static const char* program = NULL;
static int usage()
{
printf("Usage: %s [-l] CATEGORY[=LOCALE] [CATEGORY[=LOCALE] ...]\n", program);
return 1;
}
int main(int argc, char** argv)
{
LC_CATEGORY category_table;
category_table["LC_ALL"] = LC_ALL;
category_table["LC_COLLATE"] = LC_COLLATE;
category_table["LC_CTYPE"] = LC_CTYPE;
category_table["LC_MONETARY"] = LC_MONETARY;
category_table["LC_TIME"] = LC_TIME;
#ifdef LC_ADDRESS
category_table["LC_ADDRESS"] = LC_ADDRESS;
#endif
#ifdef LC_IDENTIFICATION
category_table["LC_IDENTIFICATION"] = LC_IDENTIFICATION;
#endif
#ifdef LC_MEASUREMENT
category_table["LC_MEASUREMENT"] = LC_MEASUREMENT;
#endif
#ifdef LC_MESSAGES
category_table["LC_MESSAGES"] = LC_MESSAGES;
#endif
#ifdef LC_NAME
category_table["LC_NAME"] = LC_NAME;
#endif
category_table["LC_NUMERIC"] = LC_NUMERIC;
#ifdef LC_PAPER
category_table["LC_PAPER"] = LC_PAPER;
#endif
#ifdef LC_TELEPHONE
category_table["LC_TELEPHONE"] = LC_TELEPHONE;
#endif
program = argv[0];
if (argc < 2)
return usage();
for (int i = 1; i < argc; i++)
{
LC_CATEGORY::const_iterator cit;
string arg(argv[i]);
if (arg == "-l")
{
printf("CATEGORY:\n");
for (cit = category_table.begin(); cit != category_table.end(); ++cit)
printf(" %s\n", cit->first.c_str());
continue;
}
size_t pos = arg.find('=');
bool npos = (pos < arg.npos);
string category_name(arg.substr(0, pos));
string locale(arg.substr(pos + 1));
cit = category_table.find(category_name);
if (cit == category_table.end())
{
string tmp(string("LC_") + category_name);
cit = category_table.find(tmp);
if (cit == category_table.end())
{
printf("unknown category: %s\n", category_name.c_str());
continue;
}
category_name = tmp;
}
category_name = cit->first;
int category = cit->second;
const char* pold = setlocale(category, NULL);
string oldloc(pold ? pold : "(NULL)");
if (!npos)
{
printf("setlocale(%s, NULL) = \"%s\"\n",
category_name.c_str(), oldloc.c_str());
continue;
}
const char* pnew = setlocale(category, locale.c_str());
string newloc(pnew ? pnew : "(NULL)");
printf("setlocale(%s, \"%s\"): \"%s\" -> \"%s\"\n",
category_name.c_str(), locale.c_str(),
oldloc.c_str(), newloc.c_str());
}
return 0;
}
LOCALE
の既定
既定は
setlocale(LC_ALL, "C")
の状態で、現在のロケールを設定するには main
関数などで
setlocale(LC_CATEGORY, "")
を実行することになっているようです。
LOCALE
文字列について
大文字・小文字の区別
区別されると不便なので、一般的に区別されないようですが、仕様として明示しているものは MSVC 以外に見つけられませんでした。ライブラリによっては区別されるのかもしれません。
「-
」文字の省略
Windows では省略可能ですが、他では省略できない場合があります。
Windows での setlocale
- 実行環境: Windows 11 バージョン 23H2 (CP932)
- 開発環境: MSVC v143 - VS 2022 C++
>setlocale -l all all= ctype=.UTF8 ctype=en_US.UTF-8
CATEGORY:
LC_ALL
LC_COLLATE
LC_CTYPE
LC_MONETARY
LC_NUMERIC
LC_TIME
setlocale(LC_ALL, NULL) = "C"
setlocale(LC_ALL, ""): "C" -> "Japanese_Japan.932"
setlocale(LC_CTYPE, ".UTF8"): "Japanese_Japan.932" -> "Japanese_Japan.utf8"
setlocale(LC_CTYPE, "en_US.UTF-8"): "Japanese_Japan.utf8" -> "en_US.UTF-8"
Unix 系 OS
locale -a
コマンドで利用可能なロケール名は取得できます。
Linux - Debian 11.9
$ locale -a
C
C.utf8
en_US.utf8
POSIX
少ないので、apt
で locales-all
をインストールすると
$ locale -a | grep -i en_US
en_US
en_US.iso885915
en_US.utf8
$ locale -a | grep -i ja_
ja_JP.eucjp
ja_JP.utf8
ja_JP では .eucJP と .utf8 が追加されました。
$ g++ --version
g++ (Debian 12.2.0-14) 12.2.0
〜〜〜(略)〜〜〜
$ ./setlocale -l all ctype=en_US.UTF8 ctype=en_US.UTF-8
CATEGORY:
LC_ADDRESS
LC_ALL
LC_COLLATE
LC_CTYPE
LC_IDENTIFICATION
LC_MEASUREMENT
LC_MESSAGES
LC_MONETARY
LC_NAME
LC_NUMERIC
LC_PAPER
LC_TELEPHONE
LC_TIME
setlocale(LC_ALL, NULL) = "C"
setlocale(LC_CTYPE, "en_US.UTF8"): "C" -> "en_US.UTF8"
setlocale(LC_CTYPE, "en_US.UTF-8"): "en_US.UTF8" -> "en_US.UTF-8"
locale
コマンドでは出てこない en_US.UTF-8
は通ったので、「-
」は無視されるようです。
macOS 14.5
$ clang++ --version
Apple clang version 15.0.0 (clang-1500.3.9.4)
〜〜〜(略)〜〜〜
$ ./setlocale -l all all=ja_JP.UTF-8 ctype=en_US.UTF8 ctype=en_US.UTF-8 ctype=UTF-8
CATEGORY:
LC_ALL
LC_COLLATE
LC_CTYPE
LC_MESSAGES
LC_MONETARY
LC_NUMERIC
LC_TIME
setlocale(LC_ALL, NULL) = "C"
setlocale(LC_ALL, "ja_JP.UTF-8"): "C" -> "ja_JP.UTF-8"
setlocale(LC_CTYPE, "en_US.UTF8"): "ja_JP.UTF-8" -> "(NULL)"
setlocale(LC_CTYPE, "en_US.UTF-8"): "ja_JP.UTF-8" -> "en_US.UTF-8"
setlocale(LC_CTYPE, "UTF-8"): "en_US.UTF-8" -> "UTF-8"
en_US.UTF8
はエラーなので、「-
」は省略できないようです。
UTF-8 文字種を指定する
setlocale(LC_CTYPE, "UTF-8")
がスッキリしてていいのですが、試した範囲で通るのは macOS だけでした。
UTF-8 の場合、LC_CTYPE
の locale
では、<言語>_<地域>
部分の影響は受けないハズなので、まず存在が期待できる en_US
を使って
setlocale(LC_CTYPE, "en_US.UTF8")
setlocale(LC_CTYPE, "en_US.UTF-8")
の両方を試すのがよさそうです。が、順番に試すなら
(setlocale(LC_CTYPE, ".UTF8") ||
setlocale(LC_CTYPE, "UTF8") ||
setlocale(LC_CTYPE, "UTF-8") ||
setlocale(LC_CTYPE, "C.UTF8") ||
setlocale(LC_CTYPE, "C.UTF-8") ||
setlocale(LC_CTYPE, "en_US.UTF8") ||
setlocale(LC_CTYPE, "en_US.UTF-8"));
となるでしょうか。