如何格式化C中的数字从1123456789到1,123,456,789?

goe*_*goe 73 c formatting numbers

从可我怎么用C语言格式的数字11234567891,123,456,789?我试过使用,printf("%'10d\n", 1123456789);但这不起作用.

你能告诉我什么吗?解决方案越简单越好.

Car*_*rum 71

如果你的printf支持该'标志,你可以通过适当地设置你的语言环境来做到这一点.例:

#include <stdio.h>
#include <locale.h>

int main(void)
{
    setlocale(LC_NUMERIC, "");
    printf("%'d\n", 1123456789);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

并构建和运行:

$ ./example 
1,123,456,789
Run Code Online (Sandbox Code Playgroud)

在Mac OS X和Linux(Ubuntu 10.10)上测试过.

  • 虽然这很方便 - 您不一定要更改此功能的状态 (setlocale)。 (2认同)

pax*_*blo 42

您可以按如下方式递归执行(请注意,INT_MIN如果您使用的是两个补码,则需要额外的代码来管理它):

void printfcomma2 (int n) {
    if (n < 1000) {
        printf ("%d", n);
        return;
    }
    printfcomma2 (n/1000);
    printf (",%03d", n%1000);
}

void printfcomma (int n) {
    if (n < 0) {
        printf ("-");
        n = -n;
    }
    printfcomma2 (n);
}
Run Code Online (Sandbox Code Playgroud)

一个总结:

  • 用户调用printfcomma一个整数,负数的特殊情况通过简单地打印" - "并使数字为正(这是不能使用的位INT_MIN)来处理.
  • 当您输入时printfcomma2,只有少于1,000的数字才会打印并返回.
  • 否则递归将在下一级调用(因此1,234,567将被调用1,234,然后是1),直到找到小于1,000的数字.
  • 然后将打印该数字,我们将返回递归树,打印逗号和下一个数字.

还有更简洁的版本虽然它在检查每个级别的负数时做了不必要的处理(并不是因为递归级别的数量有限,这很重要).这是一个完整的测试程序:

#include <stdio.h>

void printfcomma (int n) {
    if (n < 0) {
        printf ("-");
        printfcomma (-n);
        return;
    }
    if (n < 1000) {
        printf ("%d", n);
        return;
    }
    printfcomma (n/1000);
    printf (",%03d", n%1000);
}

int main (void) {
    int x[] = {-1234567890, -123456, -12345, -1000, -999, -1,
               0, 1, 999, 1000, 12345, 123456, 1234567890};
    int *px = x;
    while (px != &(x[sizeof(x)/sizeof(*x)])) {
        printf ("%-15d: ", *px);
        printfcomma (*px);
        printf ("\n");
        px++;
    }
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

输出是:

-1234567890    : -1,234,567,890
-123456        : -123,456
-12345         : -12,345
-1000          : -1,000
-999           : -999
-1             : -1
0              : 0
1              : 1
999            : 999
1000           : 1,000
12345          : 12,345
123456         : 123,456
1234567890     : 1,234,567,890
Run Code Online (Sandbox Code Playgroud)

对于那些不信任递归的人来说,这是一个迭代解决方案(尽管递归的唯一问题往往是堆栈空间,这在这里不会成为问题,因为即使对于64位整数,它也只有几个级别):

void printfcomma (int n) {
    int n2 = 0;
    int scale = 1;
    if (n < 0) {
        printf ("-");
        n = -n;
    }
    while (n >= 1000) {
        n2 = n2 + scale * (n % 1000);
        n /= 1000;
        scale *= 1000;
    }
    printf ("%d", n);
    while (scale != 1) {
        scale /= 1000;
        n = n2 / scale;
        n2 = n2  % scale;
        printf (",%03d", n);
    }
}
Run Code Online (Sandbox Code Playgroud)

这两个都产生2,147,483,647INT_MAX.

  • +1递归实现!好吧! (3认同)

Gre*_*ill 11

这是一个非常简单的实现.此功能包含任何错误检查,缓冲区大小必须由调用进行验证.它也不适用于负数.这些改进留给读者练习.

void format_commas(int n, char *out)
{
    int c;
    char buf[20];
    char *p;

    sprintf(buf, "%d", n);
    c = 2 - strlen(buf) % 3;
    for (p = buf; *p != 0; p++) {
       *out++ = *p;
       if (c == 1) {
           *out++ = ',';
       }
       c = (c + 1) % 3;
    }
    *--out = 0;
}
Run Code Online (Sandbox Code Playgroud)


lor*_*nix 6

Egads!我一直这样做,在linux上使用gcc/g ++和glibc,是的,'运算符可能是非标准的,但我喜欢它的简单性.

#include <stdio.h>
#include <locale.h>

int main()
{
    int bignum=12345678;

    setlocale(LC_ALL,"");

    printf("Big number: %'d\n",bignum);

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

给出的输出:

大数:12,345,678

只需记住那里的'setlocale'调用,否则它不会格式化任何东西.

  • 可悲的是,这似乎在Windows / gcc 4.9.2中不起作用。 (2认同)

Jer*_*fin 5

也许区域设置感知版本会很有趣。

#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;
        for (int i=neg_len; i>0; i--) {
            *ptr-- = neg[i-1];
            if (++len >= bufsize)
                goto ABORT;
        }
    }

    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
Run Code Online (Sandbox Code Playgroud)

这确实有一个错误(但我认为相当小)。在二进制补码硬件上,它不会正确转换最大负数,因为它尝试将负数转换为其等效的正数 在N = -N;二进制补码中,最大负数没有相应的正数,除非您将其推广为更大的类型。解决这个问题的一种方法是将数字提升为相应的无符号类型(但这有点不简单)。