The C standard does not provide formatting directly, but provides the ability to get ... a specification of what formatting should be, depending on the locale. So, you need to get the locale specification of the correct formatting, and then use it to format your data (but even then it is somewhat nontrivial). For example, here is the version for formatting long data:
#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
This has a bug (but I find it pretty minor). On two additional hardware, he will not correctly convert the most negative number, since he is trying to convert a negative number into its equivalent positive number using N = -N; . In two additions, a maximally negative number does not have a corresponding positive number unless you advance it to a larger type. One way around this is to promote the number of the corresponding unsigned type (but it is somewhat nontrivial).
The implementation for other integer types is pretty trivial. For floating point types, a bit more work. Converting floating-point types correctly (even without formatting) is a lot of work, so I would at least think of using something like sprintf to convert and then paste the formatting into the string that was created.
Jerry Coffin Jun 14 '17 at 14:26 2017-06-14 14:26
source share