ロケールを記述する 'struct lconv'の値の正式で実用的な制約は何ですか?

バックグラウンド

The C99 standard, section 7.11, describes the header and its contents. In particular, it defines struct lconv and says that:

[...] "C"ロケールでは、メンバーは   コメントに指定された値。

  char * decimal_point;//""
char * thousands_sep;//""
char * grouping;//""
char * mon_decimal_point;//""
char * mon_thousands_sep;//""
char * mon_grouping;//""
char * positive_sign;//""
char * negative_sign;//""
char * currency_symbol;//""
char frac_digits;//CHAR_MAX
char p_cs_precedes;//CHAR_MAX
char n_cs_precedes;//CHAR_MAX
char p_sep_by_space;//CHAR_MAX
char n_sep_by_space;//CHAR_MAX
char p_sign_posn;//CHAR_MAX
char n_sign_posn;//CHAR_MAX
char * int_curr_symbol;//""
char int_frac_digits;//CHAR_MAX
char int_p_cs_precedes;//CHAR_MAX
char int_n_cs_precedes;//CHAR_MAX
char int_p_sep_by_space;//CHAR_MAX
char int_n_sep_by_space;//CHAR_MAX
char int_p_sign_posn;//CHAR_MAX
char int_n_sign_posn;//CHAR_MAX
 

セクション7.11.2.1「localeconv()関数」は次のように説明します:

タイプ char * の構造体のメンバーは、文字列へのポインタです。   ( decimal_point を除く)は、その値が使用できないことを示すために ""   現在のロケールまたはゼロ長です。 [...] char型のメンバーは   非負の数値であり、値がそうでないことを示す CHAR_MAX   現在のロケールで利用可能です。

各メンバーについて議論していきます。 p_cs_precedes p_sep_by_space p_sign_posn の3つのメンバーからなる4つのグループを見ることができます。

char p_cs_precedes
  currency_symbolがそれぞれ先行する場合は1または0に設定されます。   非負のローカルフォーマットされた金額の値を継承します。

     

char p_sep_by_space
  currency_symbolの区切りを示す値に設定します。   符号付き文字列、および非負のローカル形式の通貨の値   数量

     

char p_sign_posn   aのためのpositive_signの位置を示す値に設定します。   非負のローカルフォーマットの金額

p_sign_posn の解釈の詳細は次のとおりです。この質問には重要ではありません。

また、これらの型をどのように解釈するかの例もいくつか示しています。

TC1(国際標準ISO/IEC 9899:1999テクニカルコリメンタム1、2001-09-01公開)とTC2(国際標準ISO/IEC 9899:1999)の両方が使用されていることに注意してください。 1999 Technical Corrigendum 2、2004-11-15)は、§7.11.2.1に変更を加える(しかし、TC3は変更しない)。しかし、この変更は、私が尋ねようとしている質問に対する回答には影響も及ぼさず、影響を与えません。


質問

My first two 質問 are about the four triples (cs_precedes, sep_by_space, and sign_posn), and the others more general 質問 about what constitutes a valid locale:

  1. Is it feasible or sensible to have one or two of the members of a triple with the CHAR_MAX designation while the other members have values in the normal range (0-1, 0-1, 0-4)?
  2. If it is sensible, how should the combinations be interpreted?

    Two combinations (all values set to CHAR_MAX, as in the "C" locale, and all values set validly) are defined; it is the other 6 hybrid settings that I'm curious about.

  3. Is a locale properly formed if the triples are defined but the relevant currency symbol is not?

  4. Is a locale properly formed if the monetary decimal point is not defined but the currency symbol is defined.
  5. If the sign position is not 0 (indicating that a value is surround by parentheses), is a locale properly formed if the currency symbol is set but both the positive and negative sign strings are empty?
  6. Does it make sense for the positive triple to be defined when the negative triple is not?

私の傾向は答えることです:

  1. いいえ。トリプルのメンバーのすべてまたはいずれもがCHAR_MAXに設定されている必要があります。
  2. (1)に対する回答がない場合は適用されません。
  3. いいえ
  4. いいえ(ただし、分数がなく、小数点が必要ない古いイタリア語通貨(賃貸借)の境界線の場合があります。の場合にのみ金額の小数点が必要であるという条件で処理できます> frac_digits または int_frac_digits が0より大きい場合)。
  5. いいえ
  6. いいえ

実装はこれらの規則を実施するかもしれないが、別の実装が規則を異なって解釈し、異なる結論に至ることが考えられる。

何を言ってるの?

13
「唯一の優先通貨単位と表記形式にマッチする他のロケールの確率は非常に低いです...」 - それだけでなく、ロケールの通貨記号が正しい通貨記号と一致する確率はかなり低い!ウィジェットの費用が42ポンドであれば、あなたのアプリケーションは<私>(アメリカ人)の「42ドル」ではなく「42ドル」ではないことがわかります。つまり、「$ 54.18」と表示されても大丈夫ですが、 printf はその為替レート変換を処理しないことを確信しています。 :)
追加された 著者 Quuxplusone,
うーん、あなたの立場で正義を見ることはできますが、実際はそうではありません。フォーマット関数への合理的なインターフェースには、 printf() fprintf()のような2つの変種があります。より単純なインタフェースは現在のロケールを使用しますが、1つの引数より複雑なバージョンは 'const struct lconv *'引数をとります。あなたのロケールが必要なときには、よりシンプルな関数を使います。そうでない場合は、適切に配置された struct lconv 構造体を派生させ、それを使用します。これのメリットの1つは、「ロケール提供」バージョンが厳密にテスト可能であることです。他の人はそうではありません。
追加された 著者 Jonathan Leffler,
答えの残りの半分@BRPocockは、 "ロケールを使用しない場合、通貨値をどのようにフォーマットしますか?" 「使用するフォーマットをどのように指定しますか?」私が気づいている唯一の選択肢は、バウンティノートに記載されている strfmon()関数と、そのサイドキック strmon_l() locale_t < code>を struct lconv ではなくPOSIXで定義されていますが、そうでなければコンセプトは似ています)。あなた自身の自家製コードを書く...
追加された 著者 Jonathan Leffler,
上記のどれも選ぶのは受け入れられる答えですか?貨幣の書式設定にはCのロケールを使用しないでください.1つの唯一の優先通貨単位と表記形式に一致するロケールの確率は非常に低いので...?
追加された 著者 BRFennPocock,
@Jonathan Leffler:私の(限定された)通貨フォーマットコードの経験では、プレゼンテーションデザイナーはほとんどいつもそれをフォーマットするための明示的な指示を持っていました。現在のコンテキストで通貨の適切な表示がわからない場合は、ロケールで提供されるパラメータセットが役立つとは考えにくいです(IM(NS)HO)。あなたが通貨のプレゼンテーションを知らない場合、なぜそれについて「話しているのですか? EG:通貨単位、為替レート、フォーマット、およびCを知らなくても、「ユーザーが好きなお金があれば」書式設定される特定の数字「foo」は決してありませんでした。
追加された 著者 BRFennPocock,

1 答え

形式的制約

私が知る限り、スタンダードCもPOSIXも、 struct lconv で何が有効か無効であるかに関する規則を定めていません。これのもっともらしい理由の1つは、標準CまたはPOSIXの関数が struct lconv を引数として取っていないことです。 localeconv() 関数のみが構造:

 struct lconv *localeconv(void);

Therefore, since the implementation is nominally the only source of struct lconv values, whatever the implementation does must be OK in the eyes of the implementation. All in all, it is somewhat a still-born feature; it provides functionality that nothing uses directly. Behind the scenes, though, there is support for parts of this information (think printf() and scanf() et al, for starters). The monetary information is not used by any Standard C functions. They (the header and the localeconv() and setlocale() functions) were added to C89 by the committee, in part to ensure that there could be a single ISO standard for C that would be the same as the ANSI standard for C.

Plaugerの書籍「標準Cライブラリ(C89標準ライブラリを実装しています)は、現在のロケールの規則を使用して国際通貨、国内(ローカル)通貨、および数値を書式設定するために使用できる、 _Fmtval()と呼ばれますが、使用される構造は実装によって定義され、ユーザーが提供していません。

POSIXは、 strfmon()と< code> strfmon_l() 、後者は locale_t を引数の1つとして使用します。

ssize_t strfmon(char *restrict s, size_t maxsize, const char *restrict format, ...);
ssize_t strfmon_l(char *restrict s, size_t maxsize, locale_t locale,
                  const char *restrict format, ...);

しかし、POSIXでは、 locale_t 型の内容について何も言及していませんが、制限された方法でそれらを操作するための関数を提供しています。

しかし、これらはロケールを操作するための最小限の手作業によるアプローチを提供しており、 struct lconv では何が受け入れ可能かどうかについて詳しくは触れていません。 nl_langinfo() 関数もあります:

#include 

char *nl_langinfo(nl_item item);
char *nl_langinfo_l(nl_item item, locale_t locale);

These allow you to find out, one item at a time, the values of parts of the locale, using the names such as ABDAY_1 to find out the abbreviated name of day 1, which is 'Sun' in English-speaking locales. There are some 55 such names in . Interestingly, the set is not complete; you can't find the international currency symbol this way.

実用的な制約

Given that the two primary relevant standards say nothing about the constraints on the contents of struct lconv, we are left with trying to determine 実用的な制約.

(Aside: given the symmetry of the national and international formatting information in the C99 standard, it is a pity in some respects that a structure wasn't used to encode the information; it makes for fiddly code picking the right bits and pieces out into generic functions. Some of the fields (cs_precedes, sep_by_space) could be booleans, too, but wasn't in C89.)

質問を繰り返す:

最初の2つの質問は4つのトリプル( cs_precedes sep_by_space sign_posn )に関するものです。有効なロケールを構成します:

     
      
  1. 他のメンバが通常の範囲(0-1,0-1,0-4)の値を持つ一方で、CHAR_MAX指定のトリプルのメンバーの1つまたは2つを持つことは実現可能か賢明か?
  2.   
  3. 合理的であれば、どのように組み合わせを解釈するべきですか?
  4.   
  5. トリプルが定義されているにもかかわらず、関連する通貨記号が存在しない場合、ロケールは適切に形成されていますか?
  6.   
  7. 通貨小数点が定義されていないのに通貨記号が定​​義されている場合、ロケールが正しく形成されているかどうか
  8.   
  9. 符号位置が0でない場合(値が括弧で囲まれていることを示す)、通貨記号が設定されていても正と負の両方の符号列が空の場合、ロケールは正しく形成されますか?
  10.   
  11. 負のトリプルがないときに正のトリプルが定義されるのは意味がありますか?
  12.   

オリジナルのアウトライン回答は、

      
  1. いいえ。トリプルのメンバーのすべてまたはいずれもがCHAR_MAXに設定されている必要があります。
  2.   
  3. (1)に対する回答がない場合は適用されません。
  4.   
  5. いいえ
  6.   
  7. いいえ(ただし、分数がなく、小数点が必要ない古いイタリア語通貨(賃貸借)の境界線の場合があります。の場合にのみ金額の小数点が必要であるという条件で処理できます> frac_digits または int_frac_digits が0より大きい場合)。
  8.   
  9. いいえ
  10.   
  11. いいえ
  12.   

このような書式設定を処理するためにコードを実装するのに少し時間を費やしていたので、私の元の答えは私の見解では大体正しいと言えます。

ロケールを検証するために実装したコードは次のとおりです。

/* Locale validation */
#define VALUE_IN_RANGE(v, mn, mx) ((v) >= (mn) && (v) <= (mx))
#define ASSERT(condition)           do { assert(condition); \
                                         if (!(condition)) \
                                             return false; \
                                       } while (0)
#define ASSERT_RANGE(v, mn, mx)     ASSERT(VALUE_IN_RANGE(v, mn, mx))

static bool check_decpt_thous_group(bool decpt_is_opt, const char *decpt,
                                    const char *thous, const char *group)
{
    /* Decimal point must be defined; monetary decimal point might not be */
    ASSERT(decpt != 0);
    ASSERT(decpt_is_opt || *decpt != '\0');
    /* Thousands separator and grouping must be valid (non-null) pointers */
    ASSERT(thous != 0 && group != 0);
    /* Thousands separator should be set iff grouping is set and vice versa */
    ASSERT((*thous != '\0' && *group != '\0') ||
           (*thous == '\0' && *group == '\0'));
    /* Thousands separator, if set, should be different from decimal point */
    ASSERT(*thous == '\0' || decpt_is_opt ||
          (*decpt != '\0' && strcmp(thous, decpt) != 0));
    return true;
}

static bool currency_valid(const char *currency_symbol, char frac_digits,
                           char p_cs_precedes, char p_sep_by_space, char p_sign_posn,
                           char n_cs_precedes, char n_sep_by_space, char n_sign_posn)
{
    ASSERT(currency_symbol != 0);
    if (*currency_symbol == '\0')
    {
        ASSERT(frac_digits    == CHAR_MAX);
        ASSERT(p_cs_precedes  == CHAR_MAX);
        ASSERT(p_sep_by_space == CHAR_MAX);
        ASSERT(p_sign_posn    == CHAR_MAX);
        ASSERT(n_cs_precedes  == CHAR_MAX);
        ASSERT(n_sep_by_space == CHAR_MAX);
        ASSERT(n_sign_posn    == CHAR_MAX);
    }
    else
    {
        ASSERT_RANGE(frac_digits,    0, 9);    //9 dp of currency is a lot!
        ASSERT_RANGE(p_cs_precedes,  0, 1);
        ASSERT_RANGE(p_sep_by_space, 0, 2);
        ASSERT_RANGE(p_sign_posn,    0, 4);
        ASSERT_RANGE(n_cs_precedes,  0, 1);
        ASSERT_RANGE(n_sep_by_space, 0, 2);
        ASSERT_RANGE(n_sign_posn,    0, 4);
    }
    return true;
}

static bool locale_is_consistent(const struct lconv *loc)
{
    if (!check_decpt_thous_group(false, loc->decimal_point, loc->thousands_sep, loc->grouping))
        return false;
    if (!check_decpt_thous_group((loc->frac_digits == 0 || loc->frac_digits == CHAR_MAX),
                    loc->mon_decimal_point, loc->mon_thousands_sep, loc->mon_grouping))
        return false;
    /* Signs must be valid (non-null) strings */
    ASSERT(loc->positive_sign != 0 && loc->negative_sign != 0);
    /* Signs must be different or both must be empty string (and probably n_sign_posn == 0) */
    ASSERT(strcmp(loc->positive_sign, loc->negative_sign) != 0 || *loc->negative_sign == '\0');
    if (!currency_valid(loc->currency_symbol, loc->frac_digits,
                        loc->p_cs_precedes, loc->p_sep_by_space, loc->p_sign_posn,
                        loc->n_cs_precedes, loc->n_sep_by_space, loc->n_sign_posn))
        return false;
    if (!currency_valid(loc->int_curr_symbol, loc->int_frac_digits,
                        loc->int_p_cs_precedes, loc->int_p_sep_by_space, loc->int_p_sign_posn,
                        loc->int_n_cs_precedes, loc->int_n_sep_by_space, loc->int_n_sign_posn))
        return false;
    /*
    ** If set, international currency symbol must be 3 (upper-case)
    ** alphabetic characters plus non-alphanum separator
    */
    if (*loc->int_curr_symbol != '\0')
    {
        ASSERT(strlen(loc->int_curr_symbol) == 4);
        ASSERT(isupper(loc->int_curr_symbol[0]));
        ASSERT(isupper(loc->int_curr_symbol[1]));
        ASSERT(isupper(loc->int_curr_symbol[2]));
        ASSERT(!isalnum(loc->int_curr_symbol[3]));
    }
    return true;
}

The standard says that loc->int_curr_symbol[3] is used as the 'space' character when formatting international currency, and it makes little sense to allow an alphabetic character as well as the ISO 4217 international currency code, which is three upper case letters from the basic alphabet. Allowing a digit there could lead to confusion if the sign is separate, too, so I think the !isalnum(loc->int_curr_symbol[3]) assertion is sensible. A strict check would validate that the international currency symbol is one of those listed in ISO 4217; that is a bit tricky to code, though!

0
追加された