单一 UNIX 规范版本 2 将 sprintf
的格式
'
标志行为指定为:
十进制转换结果的整数部分(我在
%i
、%d
、%u
、%f
、%g
或%G
)将被格式化为数千个分组字符[1]
c
或c++ 规范中找不到格式
'
标志。 g++ 甚至发出警告:
ISO C++11 不支持在 Visual C 中,该标志无法被识别甚至发出警告;
'
printf
标志
输出:
'd我希望能够编写符合 C 标准的代码,该代码使用
'
-flag 格式的行为。因此,我正在寻找以下答案之一:
'
-flag
'
-flag
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
这样的东西来进行转换,然后将格式插入到生成的字符串中。
#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;
}