Jak mogę w języku C formatować liczbę od 1123456789
do 1,123,456,789
? Próbowałem użyć, printf("%'10d\n", 1123456789);
ale to nie działa.
Czy mógłbyś coś doradzić? Im prostsze rozwiązanie, tym lepiej.
c
formatting
numbers
goe
źródło
źródło
printf()
rodziny sformatowanych funkcji we / wy (znak pojedynczego cudzysłowu: „) to niestandardowa flaga obsługiwana tylko w kilku implementacjach bibliotek. Szkoda, że to nie jest standard.LC_NUMERIC
. Jednak nie wiem, które locale to obsługuje.LC_NUMERIC
locale na aktualne""
sprawia, że'
działa na moim Macu i na komputerze z Linuksem, który właśnie sprawdziłem.printf()
rodziny funkcji POSIX 2008 (2013) standaryzują użycie'
znaku (pojedynczego cudzysłowu lub apostrofu) ze specyfikacjami konwersji formatowania liczb dziesiętnych, aby określić, że liczba powinna być sformatowana z separatorami tysięcy."C"
ustawieniu narodowym niepieniężny separator tysięcy jest niezdefiniowany, więc"%'d"
w"C"
ustawieniach regionalnych nie zostaną utworzone przecinki . Musisz ustawić ustawienia regionalne z odpowiednim niepieniężnym separatorem tysięcy. Częstosetlocale(LC_ALL, "");
wykona zadanie - inne wartości dla nazwy locale (inne niż pusty łańcuch) są zdefiniowane jako implementacja.Odpowiedzi:
Jeśli twój printf obsługuje
'
flagę (zgodnie z wymaganiami POSIX 2008printf()
), prawdopodobnie możesz to zrobić po prostu ustawiając odpowiednio swoje locale. Przykład:#include <stdio.h> #include <locale.h> int main(void) { setlocale(LC_NUMERIC, ""); printf("%'d\n", 1123456789); return 0; }
I buduj i uruchamiaj:
$ ./example 1,123,456,789
Przetestowano na Mac OS X i Linux (Ubuntu 10.10).
źródło
sprintf()
w systemie wbudowanym i nie działa (oczywiście, ponieważ jak mówisz, nie obsługuje flagi „.'
modyfikatora. Z nagłówka:Copyright ... 2007 Joerg Wunsch ... 1993 Regents of the University of California
tj. Pochodna BSD.Możesz to zrobić rekurencyjnie w następujący sposób (uważaj,
INT_MIN
jeśli używasz dopełnienia do dwóch, będziesz potrzebować dodatkowego kodu do zarządzania tym):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); }
Podsumowanie:
printfcomma
liczbę całkowitą, specjalny przypadek liczb ujemnych jest obsługiwany przez proste wypisanie "-" i uczynienie liczby dodatnią (jest to bit, który nie zadziałaINT_MIN
).printfcomma2
liczba mniejsza niż 1000 zostanie po prostu wydrukowana i zwrócona.Istnieje również bardziej zwięzła wersja, chociaż wykonuje niepotrzebne przetwarzanie podczas sprawdzania liczb ujemnych na każdym poziomie (nie ma to znaczenia, biorąc pod uwagę ograniczoną liczbę poziomów rekursji). Ten jest kompletnym programem do testowania:
#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; }
a wynik to:
-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
Iteracyjne rozwiązanie dla tych, którzy nie ufają rekursji (chociaż jedynym problemem związanym z rekurencją jest zwykle miejsce na stosie, które nie będzie tutaj problemem, ponieważ będzie to tylko kilka poziomów głębokości, nawet dla 64-bitowej liczby całkowitej):
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); } }
Oba generują
2,147,483,647
dlaINT_MAX
.Cały powyższy kod dotyczy grup trzycyfrowych oddzielonych przecinkami, ale możesz też użyć innych znaków, takich jak spacja:
void printfspace2 (int n) { if (n < 1000) { printf ("%d", n); return; } printfspace2 (n/1000); printf (" %03d", n%1000); } void printfspace (int n) { if (n < 0) { printf ("-"); n = -n; } printfspace2 (n); }
źródło
n
wprintfcomma
. Musisz wymusić konwersję na unsigned przed jej zanegowaniem.printf
.Oto bardzo prosta implementacja. Ta funkcja nie zawiera sprawdzania błędów, rozmiar bufora musi zostać zweryfikowany przez wywołującego. Nie działa również dla liczb ujemnych. Takie ulepszenia pozostawia się czytelnikowi jako ćwiczenie.
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; }
źródło
Egads! Robię to cały czas, używając gcc / g ++ i glibc na Linuksie i tak, operator 'może być niestandardowy, ale podoba mi się jego prostota.
#include <stdio.h> #include <locale.h> int main() { int bignum=12345678; setlocale(LC_ALL,""); printf("Big number: %'d\n",bignum); return 0; }
Daje wynik:
Duża liczba: 12,345,678
Wystarczy zapamiętać wywołanie „setlocale”, w przeciwnym razie nic nie sformatuje.
źródło
'
flagi, to nie otrzymasz żądanego wyniku - i jest to niezależne od kompilatora. Kompilator zapewnia, że funkcja biblioteki dlaprintf()
jest wywoływana za pomocą ciągu formatu; interpretacja tego zależy od funkcji biblioteki. W systemie Windows jest całkowicie możliwe, że biblioteka CRT nie zapewnia potrzebnego wsparcia - i nie ma znaczenia, którego kompilatora używasz.Być może wersja uwzględniająca locale byłaby interesująca.
#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
Ma to błąd (ale uważam go za dość niewielki). Na sprzęcie z dopełnieniem do dwóch nie przekształci poprawnie liczby najbardziej ujemnej, ponieważ próbuje zamienić liczbę ujemną na jej równoważną liczbę dodatnią z
N = -N;
uzupełnieniem do dwóch, maksymalna liczba ujemna nie ma odpowiadającej liczby dodatniej, chyba że promować go do większego typu. Jednym ze sposobów obejścia tego jest promowanie liczby odpowiadającej typowi bez znaku (ale jest to nieco nietrywialne).źródło
'
-flag tutaj: stackoverflow.com/q/44523855/2642059 Myślę, że ta odpowiedź doskonale to rozwiązuje, wykonując teraz więcej testów. Jeśli tak, to chyba powinienem oznaczyć to pytanie jako dupe, co?tsep
,place_str
ineg_str
w ogóle? Dlaczego po prostu nie użyć bezpośredniofmt_info
członków?while (*ptr-- = *neg_str++)
nie ma to dla mnie sensu. Wstawiasz ujemne znaki łańcuchowe w odwrotnej kolejności.Matematyczne podejście bez rekurencji lub obsługi ciągów:
#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) ) ; } }
Podobnie jak w przypadku rozwiązania rekurencyjnego Paxa, ale obliczając rząd wielkości z wyprzedzeniem, unika się rekursji (być może przy znacznych kosztach).
Zauważ również, że rzeczywisty znak używany do oddzielania tysięcy jest specyficzny dla lokalizacji.
Edycja : zobacz komentarze @ Chux poniżej, aby uzyskać ulepszenia.
źródło
abs(n)
nafabs(n)
zapobiega błędom komplementu 2 podczas wykonywaniaprint_number(INT_MIN)
.log10(abs(n))
a nie gdzie indziej. Co ciekawe, twoje rozwiązanie działa z pojedynczą zmianą dolog10(fabs(n))
izprint_number(INT_MIN)
powodu,printf(..., abs(n / order_of_magnitude))
co oznacza,n = abs(INT_MIN) % order_of_magnitude
że bycie negatywnym jest w porządku. Jeśli zrezygnujemy z INT_MIN,printf(..., abs(n / order_of_magnitude))
może się staćprintf(..., n / order_of_magnitude)
. Ale przypuszczam, że praca z robakiem o nazwie „abs (INT_MIN)” jest zwykle zła .log10(fabs(n))
,n = abs(n% order_of_magnitude)
iprintf(",%03d", n/order_of_magnitude)
. Przy okazji: nie wydałbym tego wysiłku, chyba że uważam, że twoje rozwiązanie jest dobre. Brak UB, nawet dla INT_MIN.Oparty na @Greg Hewgill's, ale bierze pod uwagę liczby ujemne i zwraca rozmiar ciągu.
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); }
źródło
Moja odpowiedź nie formatuje wyniku dokładnie tak, jak na ilustracji w pytaniu, ale może w niektórych przypadkach zaspokoić rzeczywistą potrzebę za pomocą prostego jednowierszowego lub makra. W razie potrzeby można go rozszerzyć, aby wygenerować więcej tysięcy grup.
Wynik będzie wyglądał na przykład następująco:
Value: 0'000'012'345
Kod:
printf("Value: %llu'%03lu'%03lu'%03lu\n", (value / 1000 / 1000 / 1000), (value / 1000 / 1000) % 1000, (value / 1000) % 1000, value % 1000);
źródło
'
standardowa notacja jest równoważna,
(przynajmniej matematycznie) w jakiejś części świata?Nie ma naprawdę prostego sposobu na zrobienie tego w C. Po prostu zmodyfikowałbym funkcję int-to-string, aby to zrobić:
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); }
źródło
Kolejna funkcja iteracyjna
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); } }
źródło
Oto najcieńsza, wydajna pod względem wielkości i szybkości implementacja tego rodzaju formatowania cyfr dziesiętnych:
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; }
Użyj w następujący sposób:
char buffer[16]; fprintf (stderr, "test : %s.", formatNumber (1234567890, buffer + 16, true));
Wynik:
test : +1,234,567,890.
Niektóre zalety:
Funkcja pobierająca koniec bufora ciągu z powodu odwrotnego uporządkowania formatowania. Wreszcie, gdzie nie ma potrzeby cofania wygenerowanego ciągu (strrev).
Ta funkcja tworzy jeden ciąg, którego można użyć w dowolnym algo po. Nie zależy ani nie wymaga wielu wywołań printf / sprintf, co jest strasznie powolne i zawsze zależy od kontekstu.
źródło
unlikely
?unlikely
prawdopodobnie jest wskazówką dla optymalizatora, że warunek prawdopodobnie nie jest prawdziwy. Aby uzyskać więcej informacji, zobaczlikely()
/unlikely()
macros w jądrze Linuksa .Bezpieczny format_commas, z liczbami ujemnymi:
Ponieważ VS <2015 nie implementuje snprintf, musisz to zrobić
#if defined(_WIN32) #define snprintf(buf,len, format,...) _snprintf_s(buf, len,len, format, __VA_ARGS__) #endif
I wtedy
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; }
Przykładowe użycie:
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);
źródło
Zmodyfikowana wersja rozwiązania @paxdiablo, ale używająca
WCHAR
iwsprinf
: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); }
źródło
Jestem nowy w programowaniu w C. Oto mój prosty kod.
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; }
źródło
#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 ... }
Moje rozwiązanie wykorzystuje plik. zamiast a, Czytelnik może to zmienić.
źródło
To jest stare i istnieje wiele odpowiedzi, ale pytanie nie brzmiało „jak napisać procedurę dodawania przecinków”, ale „jak to zrobić w C”? Komentarze wskazywały na ten kierunek, ale w moim systemie Linux z GCC działa to dla mnie:
#include <stdio.h> #include <stdlib.h> #include <locale.h> int main() { unsetenv("LC_ALL"); setlocale(LC_NUMERIC, ""); printf("%'lld\n", 3141592653589); }
Po uruchomieniu otrzymuję:
$ cc -g comma.c -o comma && ./comma 3,141,592,653,589
Jeśli wyłączę
LC_ALL
zmienną przed uruchomieniem programu,unsetenv
nie jest to konieczne.źródło
Musiałem zrobić coś podobnego sam, ale zamiast drukować bezpośrednio, musiałem przejść do bufora. Oto, co wymyśliłem. Działa wstecz.
unsigned int IntegerToCommaString(char *String, unsigned long long Integer) { unsigned int Digits = 0, Offset, Loop; unsigned long long Copy = Integer; do { Digits++; Copy /= 10; } while (Copy); Digits = Offset = ((Digits - 1) / 3) + Digits; String[Offset--] = '\0'; Copy = Integer; Loop = 0; do { String[Offset] = '0' + (Copy % 10); if (!Offset--) break; if (Loop++ % 3 == 2) String[Offset--] = ','; Copy /= 10; } while (1); return Digits; }
Należy pamiętać, że jest przeznaczony tylko dla liczb całkowitych bez znaku i należy upewnić się, że bufor jest wystarczająco duży.
źródło
Innym rozwiązaniem jest zapisanie wyniku w
int
tablicy o maksymalnym rozmiarze 7, ponieważlong long int
typ może obsługiwać liczby z zakresu od 9 223 372 036 854 775 807 do -9 223 372 036 854 775 807 . (Zauważ, że nie jest to wartość bez znaku).Nierekurencyjna funkcja drukowania
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]); } } } }
główne wywołanie funkcji
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 }
testowanie wyjścia
-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
W funkcji main ():
int numberSeparated[8]; long long int number = 1234567890LL; getNumWcommas(number, numberSeparated);
Jeśli potrzebne jest tylko drukowanie, przejdź
int numberSeparated[8];
do funkcjigetNumWcommas
i nazwij to w ten sposóbgetNumWcommas(number)
.źródło
Można to zrobić całkiem łatwo ...
//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'; }
Przykładowe połączenie:
char szBuffer[512]; pretty_number("1234567", szBuffer); //strcmp(szBuffer, "1,234,567") == 0
źródło
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(","); } }
źródło
,
liczby poniżej100
, używaprintf()
dokądputchar()
poleciałby, używa mylących nazw, chaotycznych wcięć i zdecydowanie za dużo kodu.// 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'; }
źródło
idx
może zostać usunięta. Kod nie daje nic dla 0. Nie obsługuje liczb ujemnych. Nie ma żadnego oczywistego powodu, abybuffer
sięstatic
zmienną (ogranicza ponowne wejścia kodu). Nie ma wyjaśnienia, co robi, ani nie wspomina się, że po zakończeniu kodu ciąg wskazywany przezp
zawiera sformatowany ciąg. Najmniej poważny problem polega na tym, że jako separator tysięcy używa spacji zamiast przecinka. Fakt, że nie obsługuje zera, jest jednak zabójczym problemem.