Winapi 使用默认值填充NUMBERFMT的简单方法?

Winapi 使用默认值填充NUMBERFMT的简单方法?,winapi,localization,Winapi,Localization,我正在使用Windows API格式化一些数字,以便为当前用户显示适当的本地化选项(例如,确保他们在正确的位置有正确的分隔符)。当您希望完全使用用户默认值时,这是微不足道的 但在某些情况下,我有时不得不覆盖基数分隔符后的位数。这需要提供一个结构。我想做的是调用一个API,它返回NUMBERFMT,并为用户填充适当的默认值,然后只覆盖我需要更改的字段。但似乎没有API来获取默认值 目前,我正在反复调用GetLocaleInfo,然后将数据转换为NUMBERFMT所需的格式 NUMBERFMT fm

我正在使用Windows API格式化一些数字,以便为当前用户显示适当的本地化选项(例如,确保他们在正确的位置有正确的分隔符)。当您希望完全使用用户默认值时,这是微不足道的

但在某些情况下,我有时不得不覆盖基数分隔符后的位数。这需要提供一个结构。我想做的是调用一个API,它返回NUMBERFMT,并为用户填充适当的默认值,然后只覆盖我需要更改的字段。但似乎没有API来获取默认值

目前,我正在反复调用GetLocaleInfo,然后将数据转换为NUMBERFMT所需的格式

NUMBERFMT fmt = {0};
::GetLocaleInfoEx(LOCALE_NAME_USER_DEFAULT,
                  LOCALE_IDIGITS | LOCALE_RETURN_NUMBER,
                  reinterpret_cast<LPWSTR>(&fmt.NumDigits),
                  sizeof(fmt.NumDigits)/sizeof(WCHAR));
::GetLocaleInfoEx(LOCALE_NAME_USER_DEFAULT,
                  LOCALE_ILZERO | LOCALE_RETURN_NUMBER,
                  reinterpret_cast<LPWSTR>(&fmt.LeadingZero),
                  sizeof(fmt.LeadingZero)/sizeof(WCHAR));
WCHAR szGrouping[32] = L"";
::GetLocaleInfoEx(LOCALE_NAME_USER_DEFAULT, LOCALE_SGROUPING, szGrouping,
                  ARRAYSIZE(szGrouping));
if (::lstrcmp(szGrouping, L"3;0") == 0 ||
    ::lstrcmp(szGrouping, L"3") == 0
) {
    fmt.Grouping = 3;
} else if (::lstrcmp(szGrouping, L"3;2;0") == 0) {
    fmt.Grouping = 32;
} else {
    assert(false);  // unexpected grouping string
}
WCHAR szDecimal[16] = L"";
::GetLocaleInfoEx(LOCALE_NAME_USER_DEFAULT, LOCALE_SDECIMAL, szDecimal,
                  ARRAYSIZE(szDecimal));
fmt.lpDecimalSep = szDecimal;
WCHAR szThousand[16] = L"";
::GetLocaleInfoEx(LOCALE_NAME_USER_DEFAULT, LOCALE_STHOUSAND, szThousand,
                  ARRAYSIZE(szThousand));
fmt.lpThousandSep = szThousand;
::GetLocaleInfoEx(LOCALE_NAME_USER_DEFAULT,
                  LOCALE_INEGNUMBER | LOCALE_RETURN_NUMBER,
                  reinterpret_cast<LPWSTR>(&fmt.NegativeOrder),
                  sizeof(fmt.NegativeOrder)/sizeof(WCHAR));
NUMBERFMT fmt={0};
::GetLocaleInfo(区域设置\名称\用户\默认值,
LOCALE_IDIGITS | LOCALE_RETURN_编号,
重新解释铸件(&fmt.NumDigits),
sizeof(fmt.NumDigits)/sizeof(WCHAR));
::GetLocaleInfo(区域设置\名称\用户\默认值,
区域设置| ILZERO |区域设置(返回)编号,
重新解释铸件(&fmt.LeadingZero),
sizeof(fmt.LeadingZero)/sizeof(WCHAR));
WCHAR[32]=L”“;
::GetLocaleInfo(语言环境名称用户默认,语言环境分组,szGrouping,
排列(分组));
如果(::lstrcmp(szGrouping,L“3;0”)==0||
::lstrcmp(szr,L“3”)==0
) {
fmt分组=3;
}else if(::lstrcmp(szGrouping,L“3;2;0”)==0){
fmt分组=32;
}否则{
assert(false);//意外的分组字符串
}
WCHAR szDecimal[16]=L”“;
::GetLocaleInfo(语言环境\名称\用户\默认值,语言环境\标准值,szDecimal,
数组化(szDecimal));
fmt.lpDecimalSep=szDecimal;
WCHAR SZ000[16]=L”“;
::GetLocaleInfo(语言环境名称用户默认值,语言环境STHOUSAND,sz000,
阵列化(SZ000);
fmt.LpthandSep=Sz000;
::GetLocaleInfo(区域设置\名称\用户\默认值,
LOCALE_INEGNUMBER | LOCALE_RETURN_NUMBER,
重新解释演员阵容(&fmt.NegativeOrder),
sizeof(fmt.NegativeOrder)/sizeof(WCHAR));

难道没有一个API已经做到了这一点吗?

我上周刚刚写了一些代码来做到这一点。唉,似乎没有GetDefaultNumberFormat(LCID LCID,NUMBERFMT*fmt)函数;你必须自己写,因为你已经开始写了。另一方面,分组字符串具有易于解析的定义良好的格式;您当前的代码“3”(应该是30)是错误的,并且显然会在更奇特的分组上失败(尽管这实际上可能不是什么大问题)。

如果您只想从字符串末尾截断小数位数,您可以使用一种默认格式(如
LOCALE\u NAME\u USER\u default
),然后检查结果字符串中是否存在小数分隔符(大陆语言中的逗号,英语中的点),然后通过将其替换为空字节来切掉小数部分:

#define cut_off_decimals(sz, cch) \
    if (cch >= 5 && (sz[cch-4] == _T('.') || sz[cch-4] == _T(','))) \
        sz[cch-4] = _T('\0');
(匈牙利语警报:
sz
为C字符串,
cch
为字符计数,包括终止的空字节。
\u T
char
wchar\u T
的Windows通用文本makro,具体取决于是否定义了
UNICODE
,仅用于与Windows 9x/ME兼容。)

请注意,对于用户定义格式的非常奇怪的情况,这将产生不正确的结果,其中从第三个到最后一个字符是点或逗号,对用户来说除了小数分隔符之外还有一些特殊意义。我一生中从未见过这样的数字格式,因此我得出结论,这是很好和安全的


当然,如果倒数第三个字符既不是点也不是逗号,那么这就没什么用了。

Darn,我希望有一个API。我很确定我的分组字段是正确的。看看它从哪里开始,“0到9和32范围内的值是有效的。”(因此,30是无效的。)在实践中,0,3,可能还有32都是你真正看到的。但如果你想处理所有的情况以防万一,请看一下。这是一个很好的链接,尽管它与MSDN有点矛盾,但它似乎是准确的。这对于我所追求的太具体了。我真的想选择小数位数,而不是把它们全部删掉。我可以使用与此类似的方法,但我仍然需要查找小数位数,以便知道检查基数分隔符的位置(并确保不会将其与分组分隔符混淆)。总而言之,这似乎是一个黑客行为。此外,还有一个小问题:
\u UNICODE
实际上是MS CRT宏,用于控制如何定义
\T
\u TEXT
UNICODE
是控制如何定义
文本的Windows宏。