gpt4 book ai didi

c - 描述区域设置的 'struct lconv' 中的值的正式和实际限制是什么?

转载 作者:太空狗 更新时间:2023-10-29 16:39:09 24 4
gpt4 key购买 nike

背景

C99 标准的第 7.11 节描述了 <locale.h> header 及其内容。特别是,它定义了 struct lconv 并表示:

[...] In the "C" locale, the members shall have the values specified in the comments.

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() 函数”接着说:

The members of the structure with type char * are pointers to strings, any of which (except decimal_point) can point to "", to indicate that the value is not available in the current locale or is of zero length. [...] The members with type char are nonnegative numbers, any of which can be CHAR_MAX to indicate that the value is not available in the current locale.

它继续讨论每个成员。你可以看到 4 个组,每组 3 个成员,一个代表组是 p_cs_precedesp_sep_by_spacep_sign_posn

char p_cs_precedes
Set to 1 or 0 if the currency_symbol respectively precedes or succeeds the value for a nonnegative locally formatted monetary quantity.

char p_sep_by_space
Set to a value indicating the separation of the currency_symbol, the sign string, and the value for a nonnegative locally formatted monetary quantity.

char p_sign_posn Set to a value indicating the positioning of the positive_sign for a nonnegative locally formatted monetary quantity.

给出了p_sign_posn的详细解释;它们对这个问题并不重要。

该标准还给出了一些如何解释这些类型的示例。

如果您找到原始的 C99 标准 (ISO/IEC 9899:1999),请注意 TC1(国际标准 ISO/IEC 9899:1999 技术勘误 1,发布于 2001-09-01)和 TC2(国际标准 ISO/IEC 9899:1999 Technical Corrigendum 2, published 2004-11-15) 对 §7.11.2.1 进行了更改(但 TC3 没有)。但是,这些更改既不解决也不影响我将要问的问题的答案。


问题

我的前两个问题是关于四个三元组(cs_precedes、sep_by_space 和 sign_posn),而其他更一般的问题是关于什么构成有效语言环境:

  1. 让三元组中的一个或两个成员指定 CHAR_MAX 而其他成员的值在正常范围内(0-1、0-1、0-4)是否可行或明智?
  2. 如果合理,应该如何解释这些组合?

    定义了两个组合(所有值都设置为 CHAR_MAX ,如在 "C" 语言环境中,并且所有值都有效设置);我很好奇的是其他 6 个混合设置。

  3. 如果定义了三元组但相关的货币符号没有定义,那么区域设置是否正确?

  4. 如果未定义货币小数点但定义了货币符号,则语言环境的格式是否正确。
  5. 如果符号位置不为 0(表示值被括号括起来),如果设置了货币符号但正负号字符串均为空,则语言环境是否正确形成?
  6. 定义正三元组而不定义负三元组是否有意义?

我的倾向是回答:

  1. 没有;三元组的所有成员或所有成员都不应设置为 CHAR_MAX。
  2. 鉴于 (1) 的答案不适用。
  3. 没有。
  4. 否(但对于旧的意大利货币(里拉)有一个边界情况,其中没有分数,因此不需要小数点;这可以通过以下条件处理:仅当 frac_digits 或时才需要货币小数点int_frac_digits 大于零)。
  5. 没有。
  6. 没有。

一个实现可能会强制执行这些规则,但可以想象另一个实现会以不同的方式解释规则并得出不同的结论。

你怎么说?

最佳答案

形式约束

据我所知,标准 C 和 POSIX 都没有规定任何关于在 struct lconv 中有效和无效的规则。 .一个可能的原因是标准 C 或 POSIX 中没有函数采用 struct lconv。作为论点;只有 localeconv() 函数返回结构:

 struct lconv *localeconv(void);

因此,由于实现名义上是 struct lconv 的唯一来源值(value)观,无论实现做什么,在实现眼中都必须是好的。总而言之,它有点像胎死腹中的特征;它提供了没有任何东西可以直接使用的功能。然而,在幕后,部分信息得到了支持(首先想想 printf()scanf() 等)。货币信息不被任何标准 C 函数使用。它们(<locale.h> header 和 localeconv()setlocale() 函数)由委员会添加到 C89,部分是为了确保 C 可以有一个单一的 ISO 标准,与 ANSI 标准相同C.

Plauger 的书 ' The Standard C Library '(它实现了 C89 标准库)提供了一个名为 _Fmtval() 的函数它可用于使用当前语言环境的约定来格式化国际货币、国家(本地)货币和数字,但再次强调,所使用的结构由实现定义,而不是由用户提供。

POSIX 确实提供了一对函数 strfmon() and strfmon_l() ,后者需要 locale_t 作为论点之一。

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 <langinfo.h>

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

这些允许您使用诸如 ABDAY_1 之类的名称,一次找到一个项目,区域设置的各个部分的值。找出第 1 天的缩写名称,在英语语言环境中为“Sun”。 <langinfo.h> 中大约有 55 个这样的名字.有趣的是,这套装置并不完整;您无法通过这种方式找到国际货币符号。

实际限制

鉴于两个主要相关标准都没有对struct lconv的内容进行约束。 ,我们只剩下尝试确定实际约束。

(旁白:考虑到 C99 标准中国家和国际格式化信息的对称性,在某些方面遗憾的是没有使用结构来编码信息;它使得繁琐的代码将正确的点点滴滴挑选成通用函数。一些字段( cs_precedessep_by_space )也可能是 bool 值,但 <stdbool.h> 不在 C89 中。)

重述问题:

My first two questions are about the four triples (cs_precedes, sep_by_space, and sign_posn), and the others more general questions 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?
  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. No; either all or none of the members of a triple should be set to CHAR_MAX.
  2. Not applicable given the answer to (1).
  3. No.
  4. No (but there is a borderline case for the old Italian currency (lire) where there were no fractions and so no decimal point was needed; that could be handled with a condition that the monetary decimal point is only needed if frac_digits or int_frac_digits is greater than zero).
  5. No.
  6. No.

我花了一些时间实现代码来处理这样的格式设置,在我看来,我原来的答案基本上是正确的。

我最终实现的验证语言环境的代码是:

/* 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;
}

标准说 loc->int_curr_symbol[3]在格式化国际货币时用作“空格”字符,允许字母字符以及 ISO 4217 国际货币代码(基本字母表中的三个大写字母)毫无意义。如果符号也是分开的,那么允许数字可能会导致混淆,所以我认为 !isalnum(loc->int_curr_symbol[3])断言是明智的。严格检查将验证国际货币符号是否是 ISO 4217 中列出的符号之一;不过,这在编码上有点棘手!

关于c - 描述区域设置的 'struct lconv' 中的值的正式和实际限制是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8261684/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com