跨平台支持 sprintf 的格式'-Flag

问题描述 投票:0回答:2

单一 UNIX 规范版本 2sprintf

 的格式 
'
 标志行为指定为:

十进制转换结果的整数部分(

%i

%d
%u
%f
%g
%G
)将被格式化为数千个分组字符
[1]

我在

c

c++'g++ 甚至发出警告:

ISO C++11 不支持

'

 
printf
 标志

在 Visual C 中,该标志无法被识别甚至发出警告;

printf("%'d", foo)

输出
:

'd

我希望能够编写符合 C 标准的代码,该代码使用

'

-flag 格式的行为。因此,我正在寻找以下答案之一:

    C-格式的标准规范
  1. '
    -flag
  2. gcc 格式的跨平台兼容外推
  3. '
    -flag
  4. 证明跨平台外推是不可能的
c printf cross-platform number-formatting sus
2个回答
2
投票
标准 C 不直接提供格式化功能,但它确实提供了检索特定于语言环境的格式化规范的能力。因此,您需要检索区域设置的正确格式规范,然后将其用于格式化您的数据(但即便如此,这也不是一件简单的事情)。例如,这是一个用于格式化

long

 数据的版本:

#include <stdlib.h> #include <locale.h> #include <string.h> #include <limits.h> static int next_group(char const **grouping) { if ((*grouping)[1] == CHAR_MAX) return 0; if ((*grouping)[1] != '\0') ++*grouping; return **grouping; } size_t commafmt(char *buf, /* Buffer for formatted string */ int bufsize, /* Size of buffer */ long N) /* Number to convert */ { int i; int len = 1; int posn = 1; int sign = 1; char *ptr = buf + bufsize - 1; struct lconv *fmt_info = localeconv(); char const *tsep = fmt_info->thousands_sep; char const *group = fmt_info->grouping; // char const *neg = fmt_info->negative_sign; size_t sep_len = strlen(tsep); size_t group_len = strlen(group); // size_t neg_len = strlen(neg); int places = (int)*group; if (bufsize < 2) { ABORT: *buf = '\0'; return 0; } *ptr-- = '\0'; --bufsize; if (N < 0L) { sign = -1; N = -N; } for ( ; len <= bufsize; ++len, ++posn) { *ptr-- = (char)((N % 10L) + '0'); if (0L == (N /= 10L)) break; if (places && (0 == (posn % places))) { places = next_group(&group); for (int i=sep_len; i>0; i--) { *ptr-- = tsep[i-1]; if (++len >= bufsize) goto ABORT; } } if (len >= bufsize) goto ABORT; } if (sign < 0) { if (len >= bufsize) goto ABORT; *ptr-- = '-'; ++len; } memmove(buf, ++ptr, len + 1); return (size_t)len; } #ifdef TEST #include <stdio.h> #define elements(x) (sizeof(x)/sizeof(x[0])) void show(long i) { char buffer[32]; commafmt(buffer, sizeof(buffer), i); printf("%s\n", buffer); commafmt(buffer, sizeof(buffer), -i); printf("%s\n", buffer); } int main() { long inputs[] = {1, 12, 123, 1234, 12345, 123456, 1234567, 12345678 }; for (int i=0; i<elements(inputs); i++) { setlocale(LC_ALL, ""); show(inputs[i]); } return 0; } #endif

这确实有一个错误(但我认为相当小)。在补码硬件上,它不会正确转换最大负数,因为它尝试使用

N = -N;

 将负数转换为其等效的正数。在补码中,最大负数没有对应的正数,除非您将其提升为更大的类型。解决这个问题的一种方法是将数字提升为相应的无符号类型(但这有点不简单)。

对其他整数类型实现相同的操作相当简单。对于浮点类型来说需要更多的工作。正确地转换浮点类型(即使没有格式化)对于他们来说已经足够多了,我至少会考虑使用像

sprintf

 这样的东西来进行转换,然后将格式插入到生成的字符串中。 


0
投票
这是一个改进版本。它需要一个(双精度)值和(整数)修复, 并返回一个带有格式化整数部分的字符串,四舍五入到“固定”小数位。

#include <stdlib.h> #include <locale.h> #include <string.h> #include <limits.h> #include <inttypes.h> #include <stdint.h> static inline int next_group(char const **grouping) { if ((*grouping)[1] == CHAR_MAX) return 0; if ((*grouping)[1] != '\0') ++*grouping; return **grouping; } static size_t commafmt_inner(char *buf, int bufsize, double val, int fix) { struct lconv *fmt_info = localeconv(); char const *tsep = fmt_info->thousands_sep; char const *group = fmt_info->grouping; int neg = val < 0.; uint64_t N = neg ? (uint64_t)-val : (uint64_t)val; size_t sep_len = strlen(tsep); int places = (int)*group, len = 0, posn = 0; char *ptr; if (--bufsize < 1) return 0; ptr = buf + bufsize; *ptr-- = '\0'; while (1) { *ptr-- = (char)((N % 10L) + '0'); if (!(N /= 10L)) break; if (++len >= bufsize) return 0; if (places && !(++posn % places)) { places = next_group(&group); for (int i = sep_len; i--; *ptr-- = tsep[i]) if (++len >= bufsize) return 0; } } if (neg) { if (++len >= bufsize) return 0; *ptr-- = '-'; } memmove(buf, ++ptr, len + 2); if (fix > 0) { int fixlen; val = !neg ? val - (double)((uint64_t)val) : -(val - (double)((uint64_t)val)); snprintf(buf + len + 1, bufsize - len, "%.*f", fix, val); fixlen = strlen(buf + len); memmove(buf + len + 1, buf + len + 2, fixlen - 1); } return len; } char *commafmt(double val, int fix) { char buff[1024]; size_t len = commafmt_inner(buff, 1024, val, fix); return len ? strdup(buff) : NULL; }
    
© www.soinside.com 2019 - 2024. All rights reserved.