0
0

setlocale の動作

Last updated at Posted at 2024-05-23

setlocale テスト プログラム

コマンドライン引数に CATEGORY[=LOCALE] を並べて動作を調べる簡単なプログラムです。

CATEGORY の "LC_" は省略可能で、大文字・小文字は区別しません。

引数が CATEGORY では

setlocale(CATEGORY, NULL)

の結果を表示します。CATEGORY=LOCALE の形式では上記に加えて

setlocale(CATEGORY, LOCALE)

の結果も表示します。

オプション -l は使用可能なカテゴリ表を出力します。

setlocale.cpp
#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コマンド
$ locale -a
C
C.utf8
en_US.utf8
POSIX

少ないので、aptlocales-all をインストールすると

localeコマンド
$ 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_CTYPElocale では、<言語>_<地域> 部分の影響は受けないハズなので、まず存在が期待できる en_US を使って

setlocale(LC_CTYPE, "en_US.UTF8")
setlocale(LC_CTYPE, "en_US.UTF-8")

の両方を試すのがよさそうです。が、順番に試すなら

LC_CTYPEを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"));

となるでしょうか。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0