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

我怎样才能在C语言格式的数字从11234567891,123,456,789 ? 我尝试使用printf("%'10d\n", 1123456789); 但是这不起作用。

你能提供任何build议吗? 解决scheme越简单越好。

如果你的printf支持' flag”,你可以通过恰当的设置你的locale来完成。 例:

 #include <stdio.h> #include <locale.h> int main(void) { setlocale(LC_NUMERIC, ""); printf("%'d\n", 1123456789); return 0; } 

并build立和运行:

 $ ./example 1,123,456,789 

在Mac OS X和Linux上testing(Ubuntu 10.10)。

你可以按照下面的方式recursion地执行它(如果你使用二进制补码,请注意INT_MIN ,你需要额外的代码来pipe理它):

 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); } 

一个summmary:

  • 用户用一个整数调用printfcomma ,负数的特殊情况是通过简单地打印“ – ”并将数字设置为正值(这是INT_MIN不起作用的位)。
  • 当你inputprintfcomma2 ,一个小于1000的数字就会打印并返回。
  • 否则recursion将在下一级调用(所以1,234,567将被调用1,234,然后是1),直到find一个小于1,000的数字。
  • 然后,这个数字将被打印,我们将回到recursion树上,打印一个逗号和下一个数字。

还有更简洁的版本,尽pipe它在检查每个级别的负数时进行了不必要的处理(不是因为recursion级数有限,这将是重要的)。 这是一个完整的testing程序:

 #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; } 

输出是:

 -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 

对于那些不信任recursion的人来说,这是一个迭代的解决scheme(尽piperecursion唯一的问题往往是堆栈空间,在这里不会有问题,因为即使是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); } } 

这两者都为INT_MAX产生2,147,483,647

这是一个非常简单的实现。 此函数不包含错误检查,缓冲区大小必须由调用者validation。 它也不适用于负数。 这样的改进留给读者来练习。

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

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

给出的输出:

大数:12,345,678

只需要记住那里的“setlocale”调用,否则将不会格式化任何东西。

也许一个语言环境感知的版本会很有趣。

 #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 

这确实有一个错误(但我认为很小)。 在二进制补码硬件上,它不会正确地转换最大负数,因为它试图将一个负数转换为它的等价正数, N = -N; 在二进制补码中,最大负数不具有相应的正数,除非将其提升为更大的types。 解决这个问题的一种方法是通过提升相应的无符号types的数字(但是这有点不重要)。

没有recursion或string处理,一个math方法:

 #include <stdio.h> #include <math.h> void print_number( int n ) { int order_of_magnitude = (n == 0) ? 1 : (int)pow( 10, ((int)floor(log10(abs(n))) / 3) * 3 ) ; printf( "%d", n / order_of_magnitude ) ; for( n = abs( n ) % order_of_magnitude, order_of_magnitude /= 1000; order_of_magnitude > 0; n %= order_of_magnitude, order_of_magnitude /= 1000 ) { printf( ",%03d", abs(n / order_of_magnitude) ) ; } } 

原则上类似于Pax的recursion解决scheme,但是通过预先计算数量级,可以避免recursion(可能花费相当大的代价)。

另请注意,用于分隔数千的实际字符是特定于语言环境的。

编辑 :请参阅下面的@ Chux的评论改进。

基于@Greg Hewgill的,但考虑到负数,并返回string大小。

 size_t str_format_int_grouped(char dst[16], int num) { char src[16]; char *p_src = src; char *p_dst = dst; const char separator = ','; int num_len, commas; num_len = sprintf(src, "%d", num); if (*p_src == '-') { *p_dst++ = *p_src++; num_len--; } for (commas = 2 - num_len % 3; *p_src; commas = (commas + 1) % 3) { *p_dst++ = *p_src++; if (commas == 1) { *p_dst++ = separator; } } *--p_dst = '\0'; return (size_t)(p_dst - dst); } 

另一个迭代function

 int p(int n) { if(n < 0) { printf("-"); n = -n; } int a[sizeof(int) * CHAR_BIT / 3] = { 0 }; int *pa = a; while(n > 0) { *++pa = n % 1000; n /= 1000; } printf("%d", *pa); while(pa > a + 1) { printf(",%03d", *--pa); } } 

下面是这种十进制数格式的最简单,最大的和最快速的实现:

 const char *formatNumber ( int value, char *endOfbuffer, bool plus) { int savedValue; int charCount; savedValue = value; if (unlikely (value < 0)) value = - value; *--endOfbuffer = 0; charCount = -1; do { if (unlikely (++charCount == 3)) { charCount = 0; *--endOfbuffer = ','; } *--endOfbuffer = (char) (value % 10 + '0'); } while ((value /= 10) != 0); if (unlikely (savedValue < 0)) *--endOfbuffer = '-'; else if (unlikely (plus)) *--endOfbuffer = '+'; return endOfbuffer; } 

使用如下:

 char buffer[16]; fprintf (stderr, "test : %s.", formatNumber (1234567890, buffer + 16, true)); 

输出:

 test : +1,234,567,890. 

一些优点:

  • 函数以string缓冲区结束,因为反向sorting格式化。 最后,无需在生成的string(strrev)中进行混响。

  • 这个函数产生一个可以在任何algorithm中使用的string。 它不依赖也不需要多个printf / sprintf调用,这是非常慢,总是上下文特定的。

  • 除法运算符的最小数目(/,%)。

另一种解决办法是,将结果保存到一个int数组中,如果因为长整型长度为7而最大值可以处理9,223,372,036,854,775,807到-9,223,372,036,854,775,807范围内的数字, note it is not an unsigned

非recursion打印function

 static void printNumber (int numbers[8], int loc, int negative) { if (negative) { printf("-"); } if (numbers[1]==-1)//one number { printf("%d ", numbers[0]); } else { printf("%d,", numbers[loc]); while(loc--) { if(loc==0) {// last number printf("%03d ", numbers[loc]); break; } else { // number in between printf("%03d,", numbers[loc]); } } } } 

主要function调用

 static void getNumWcommas (long long int n, int numbers[8]) { int i; int negative=0; if (n < 0) { negative = 1; n = -n; } for(i = 0; i<7; i++) { if (n < 1000) { numbers[i] = n; numbers[i+1] = -1; break; } numbers[i] = n%1000; n/=1000; } printNumber(numbers, i, negative);// non recursive print } 

testing输出

 -9223372036854775807: -9,223,372,036,854,775,807 -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 9223372036854775807 : 9,223,372,036,854,775,807 

在main()类中

 int numberSeperated[8]; long long int number = 1234567890LL; getNumWcommas(number, numberSeperated ); 

如果需要打印,则移动int numberSeperated[8]; 里面的函数getNumWcommas并以这种方式调用getNumWcommas(number);

我的回答并不像问题中的说明那样格式化结果,但是在一些情况下可以用简单的单行或macros来满足实际需要 。 可以根据需要扩展它以生成更多的千个组。

结果将如下查找:

Value: 0'000'012'345

代码:

 printf("Value: %llu'%03lu'%03lu'%03lu\n", (value / 1000 / 1000 / 1000), (value / 1000 / 1000) % 1000, (value / 1000) % 1000, value % 1000); 

安全的format_commas,负数:

因为VS <2015没有实现snprintf,所以你需要这样做

 #if defined(_WIN32) #define snprintf(buf,len, format,...) _snprintf_s(buf, len,len, format, __VA_ARGS__) #endif 

接着

 char* format_commas(int n, char *out) { int c; char buf[100]; char *p; char* q = out; // Backup pointer for return... if (n < 0) { *out++ = '-'; n = abs(n); } snprintf(buf, 100, "%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; return q; } 

用法示例:

 size_t currentSize = getCurrentRSS(); size_t peakSize = getPeakRSS(); printf("Current size: %d\n", currentSize); printf("Peak size: %d\n\n\n", peakSize); char* szcurrentSize = (char*)malloc(100 * sizeof(char)); char* szpeakSize = (char*)malloc(100 * sizeof(char)); printf("Current size (f): %s\n", format_commas((int)currentSize, szcurrentSize)); printf("Peak size (f): %s\n", format_commas((int)currentSize, szpeakSize)); free(szcurrentSize); free(szpeakSize); 

在C中没有真正简单的方法。我只是修改一个int-to-string函数来做到这一点:

 void format_number(int n, char * out) { int i; int digit; int out_index = 0; for (i = n; i != 0; i /= 10) { digit = i % 10; if ((out_index + 1) % 4 == 0) { out[out_index++] = ','; } out[out_index++] = digit + '0'; } out[out_index] = '\0'; // then you reverse the out string as it was converted backwards (it's easier that way). // I'll let you figure that one out. strrev(out); } 

@paxdiablo解决scheme的修改版本,但使用WCHARwsprinf

 static WCHAR buffer[10]; static int pos = 0; void printfcomma(const int &n) { if (n < 0) { wsprintf(buffer + pos, TEXT("-")); pos = lstrlen(buffer); printfcomma(-n); return; } if (n < 1000) { wsprintf(buffer + pos, TEXT("%d"), n); pos = lstrlen(buffer); return; } printfcomma(n / 1000); wsprintf(buffer + pos, TEXT(",%03d"), n % 1000); pos = lstrlen(buffer); } void my_sprintf(const int &n) { pos = 0; printfcomma(n); } 

我是C编程新手。 这是我简单的代码。

 int main() { // 1223 => 1,223 int n; int a[10]; printf(" n: "); scanf_s("%d", &n); int i = 0; while (n > 0) { int temp = n % 1000; a[i] = temp; n /= 1000; i++; } for (int j = i - 1; j >= 0; j--) { if (j == 0) { printf("%d.", a[j]); } else printf("%d,",a[j]); } getch(); return 0; } 
 #include <stdio.h> void punt(long long n){ char s[28]; int i = 27; if(n<0){n=-n; putchar('-');} do{ s[i--] = n%10 + '0'; if(!(i%4) && n>9)s[i--]='.'; n /= 10; }while(n); puts(&s[++i]); } int main(){ punt(2134567890); punt(987); punt(9876); punt(-987); punt(-9876); punt(-654321); punt(0); punt(1000000000); punt(0x7FFFFFFFFFFFFFFF); punt(0x8000000000000001); // -max + 1 ... } 

我的解决scheme使用了。 而不是一个,这是留给读者来改变这一点。

可以做得很容易…

 //Make sure output buffer is big enough and that input is a valid null terminated string void pretty_number(const char* input, char * output) { int iInputLen = strlen(input); int iOutputBufferPos = 0; for(int i = 0; i < iInputLen; i++) { if((iInputLen-i) % 3 == 0 && i != 0) { output[iOutputBufferPos++] = ','; } output[iOutputBufferPos++] = input[i]; } output[iOutputBufferPos] = '\0'; } 

示例调用:

 char szBuffer[512]; pretty_number("1234567", szBuffer); //strcmp(szBuffer, "1,234,567") == 0 
 void printfcomma ( long long unsigned int n) { char nstring[100]; int m; int ptr; int i,j; sprintf(nstring,"%llu",n); m=strlen(nstring); ptr=m%3; if (ptr) { for (i=0;i<ptr;i++) // print first digits before comma printf("%c", nstring[i]); printf(","); } j=0; for (i=ptr;i<m;i++) // print the rest inserting commas { printf("%c",nstring[i]); j++; if (j%3==0) if(i<(m-1)) printf(","); } } 
  // separate thousands int digit; int idx = 0; static char buffer[32]; char* p = &buffer[32]; *--p = '\0'; for (int i = fCounter; i != 0; i /= 10) { digit = i % 10; if ((p - buffer) % 4 == 0) *--p = ' '; *--p = digit + '0'; }