Wie kann ich in C eine große Zahl aus zB formatieren? 1123456789
zu 1,123,456,789
?
Ich habe versucht, mit printf("%'10d\n", 1123456789)
aber das geht nicht.
Können Sie etwas raten? Je einfacher die Lösung, desto besser.
geh
Wie kann ich in C eine große Zahl aus zB formatieren? 1123456789
zu 1,123,456,789
?
Ich habe versucht, mit printf("%'10d\n", 1123456789)
aber das geht nicht.
Können Sie etwas raten? Je einfacher die Lösung, desto besser.
Karl Norum
Wenn Ihr printf das unterstützt '
Flag (wie von POSIX 2008 gefordert printf()
), können Sie dies wahrscheinlich tun, indem Sie Ihr Gebietsschema entsprechend einstellen. Beispiel:
#include <stdio.h>
#include <locale.h>
int main(void)
{
setlocale(LC_NUMERIC, "");
printf("%'d\n", 1123456789);
return 0;
}
Und bauen & ausführen:
$ ./example
1,123,456,789
Getestet auf Mac OS X und Linux (Ubuntu 10.10).
Ich habe das auf getestet sprintf()
in einem eingebetteten System und es funktioniert nicht (offensichtlich, weil es, wie Sie sagen, das Flag ‘ nicht unterstützt.
– gbmhunter
8. Mai 2013 um 2:32 Uhr
Ich bin sicher, dass Sie eine C-Bibliothek finden können, die dies ohne allzu große Probleme unterstützt.
– Karl Norum
8. Mai 2013 um 3:05 Uhr
Ich habe kurz nachgesehen, nichts Passendes gefunden und meine eigene mit einigen der oben genannten Ideen umgesetzt. Es wäre großartig, eine aktuelle Bibliothek zu finden, damit Sie sie für Floats und Strings mit Dezimalstellen verwenden können.
– gbmhunter
9. Mai 2013 um 10:40 Uhr
FWIW Atmel Studio‘s eingebettetes System printf() erscheint tragisch nicht die zu unterstützen '
Modifikator. Aus der Überschrift: Copyright ... 2007 Joerg Wunsch ... 1993 Regents of the University of California
dh ein BSD-Derivat.
– Bob Stein
7. März 2014 um 16:25 Uhr
Dies ist zwar praktisch, aber Sie möchten den Status für diese Funktionalität nicht unbedingt ändern (setlocale).
– Ideengeber42
8. Oktober 2015 um 4:44 Uhr
paxdiablo
Sie können dies rekursiv wie folgt tun (Achtung INT_MIN
Wenn Sie das Zweierkomplement verwenden, benötigen Sie zusätzlichen Code, um dies zu verwalten):
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);
}
Eine Zusammenfassung:
printfcomma
Bei einer Ganzzahl wird der Sonderfall negativer Zahlen behandelt, indem einfach “-” gedruckt und die Zahl positiv gemacht wird (dies ist das Bit, mit dem es nicht funktioniert INT_MIN
).printfcomma2
eine Zahl unter 1.000 wird einfach gedruckt und zurückgegeben.Es gibt auch die prägnantere Version, obwohl sie bei der Überprüfung auf negative Zahlen unnötige Verarbeitung durchführt jeder Ebene (nicht, dass dies angesichts der begrenzten Anzahl von Rekursionsebenen von Bedeutung wäre). Dies ist ein vollständiges Programm zum Testen:
#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;
}
und die Ausgabe ist:
-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
Eine iterative Lösung für diejenigen, die der Rekursion nicht vertrauen (obwohl das einzige Problem bei der Rekursion eher der Stapelspeicher ist, der hier kein Problem darstellt, da er selbst für eine 64-Bit-Ganzzahl nur wenige Ebenen tief ist):
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);
}
}
Beides generiert 2,147,483,647
zum INT_MAX
.
Der gesamte obige Code dient zum Trennen von dreistelligen Gruppen durch Kommas, aber Sie können auch andere Zeichen verwenden, z. B. ein Leerzeichen:
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);
}
Hier ist eine sehr einfache Implementierung. Diese Funktion enthält nein Fehlerüberprüfung, Puffergrößen müssen vom Aufrufer überprüft werden. Es funktioniert auch nicht für negative Zahlen. Solche Verbesserungen werden dem Leser als Übung überlassen.
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;
}
Ich mag dieses, es verwendet sprintf anstelle von printf, was für eingebettete Systeme nützlich ist.
– gbmhunter
8. Mai 2013 um 2:04 Uhr
Ganz nett, aber mit einigen kleinen Anpassungen, um für negative Zahlen zu arbeiten.
– Ideengeber42
17. Juli 2014 um 4:36 Uhr
(modifizierte Version zur Unterstützung negativer Zahlen stackoverflow.com/a/24795133/432509)
– Ideengeber42
8. Oktober 2015 um 4:37 Uhr
Eier! Ich mache das die ganze Zeit, benutze gcc/g++ und glibc unter Linux und ja, der ‘-Operator ist vielleicht kein Standard, aber ich mag seine Einfachheit.
#include <stdio.h>
#include <locale.h>
int main()
{
int bignum=12345678;
setlocale(LC_ALL,"");
printf("Big number: %'d\n",bignum);
return 0;
}
Gibt Ausgabe von:
Große Zahl: 12.345.678
Man muss sich nur an den ‘setlocale’-Aufruf erinnern, sonst wird nichts formatiert.
Jerry Sarg
Vielleicht wäre eine Locale-fähige Version interessant.
#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
Dies hat einen Fehler (aber einen, den ich für ziemlich geringfügig halten würde). Auf Zweierkomplement-Hardware konvertiert es die negativste Zahl nicht richtig, weil es versucht, eine negative Zahl in ihre entsprechende positive Zahl mit umzuwandeln N = -N;
Im Zweierkomplement hat die maximal negative Zahl keine entsprechende positive Zahl, es sei denn, Sie heben sie auf einen größeren Typ hoch. Eine Möglichkeit, dies zu umgehen, besteht darin, die Nummer zum entsprechenden vorzeichenlosen Typ zu machen (aber das ist nicht trivial).
Ich habe eine Frage gestellt, die eher auf eine plattformübergreifende Implementierung des Formats abzielt '
-flag hier: stackoverflow.com/q/44523855/2642059 Ich denke, diese Antwort spricht das perfekt an und führt jetzt weitere Tests durch. Wenn ja, sollte ich diese Frage als Dupe markieren, oder?
– Jonathan Mei
13. Juni 2017 um 14:40 Uhr
OK, als erstes habe ich festgestellt, dass es sich nicht anpasst, wenn sich das Gebietsschema anpasst. Warum pflegen, tsep
, place_str
und neg_str
überhaupt? Warum nicht einfach direkt verwenden fmt_info
s Mitglieder?
– Jonathan Mei
13. Juni 2017 um 17:18 Uhr
OK, Zahlensache Nummer 2, dieser Code kann keine negativen Zahlen verarbeiten … und ich weiß nicht genau, wie er könnte, while (*ptr-- = *neg_str++)
macht für mich nicht viel Sinn. Sie fügen die negativen Zeichenfolgenzeichen in umgekehrter Reihenfolge ein.
– Jonathan Mei
13. Juni 2017 um 18:19 Uhr
Also … ich habe das Speicherleck beseitigt und den Fehler mit negativen Zahlen korrigiert: ideone.com/gTv8Z4 Leider gibt es immer noch ein Problem mit mehreren Trennzeichen oder negativen Symbolen mit mehreren Zeichen, die rückwärts in die Zeichenfolge geschrieben werden. Das versuche ich als nächstes zu lösen…
– Jonathan Mei
13. Juni 2017 um 19:23 Uhr
@JonathanMee: Ich habe den Code aktualisiert (und mindestens ein paar weitere Testfälle hinzugefügt, einschließlich negativer Zahlen).
– Jerry Sarg
14. Juni 2017 um 6:07 Uhr
Ohne Rekursion oder String-Handling, ein mathematischer Ansatz:
#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) ) ;
}
}
Im Prinzip ähnlich der rekursiven Lösung von Pax, aber durch vorherige Berechnung der Größenordnung wird eine Rekursion vermieden (vielleicht mit erheblichen Kosten).
Beachten Sie auch, dass das tatsächliche Zeichen, das zum Trennen von Tausendern verwendet wird, gebietsschemaspezifisch ist.
Bearbeiten:Siehe die Kommentare von @Chux unten für Verbesserungen.
Ich habe eine Frage gestellt, die eher auf eine plattformübergreifende Implementierung des Formats abzielt '
-flag hier: stackoverflow.com/q/44523855/2642059 Ich denke, diese Antwort spricht das perfekt an und führt jetzt weitere Tests durch. Wenn ja, sollte ich diese Frage als Dupe markieren, oder?
– Jonathan Mei
13. Juni 2017 um 14:40 Uhr
OK, als erstes habe ich festgestellt, dass es sich nicht anpasst, wenn sich das Gebietsschema anpasst. Warum pflegen, tsep
, place_str
und neg_str
überhaupt? Warum nicht einfach direkt verwenden fmt_info
s Mitglieder?
– Jonathan Mei
13. Juni 2017 um 17:18 Uhr
OK, Zahlensache Nummer 2, dieser Code kann keine negativen Zahlen verarbeiten … und ich weiß nicht genau, wie er könnte, while (*ptr-- = *neg_str++)
macht für mich nicht viel Sinn. Sie fügen die negativen Zeichenfolgenzeichen in umgekehrter Reihenfolge ein.
– Jonathan Mei
13. Juni 2017 um 18:19 Uhr
Also … ich habe das Speicherleck beseitigt und den Fehler mit negativen Zahlen korrigiert: ideone.com/gTv8Z4 Leider gibt es immer noch ein Problem mit mehreren Trennzeichen oder negativen Symbolen mit mehreren Zeichen, die rückwärts in die Zeichenfolge geschrieben werden. Das versuche ich als nächstes zu lösen…
– Jonathan Mei
13. Juni 2017 um 19:23 Uhr
@JonathanMee: Ich habe den Code aktualisiert (und mindestens ein paar weitere Testfälle hinzugefügt, einschließlich negativer Zahlen).
– Jerry Sarg
14. Juni 2017 um 6:07 Uhr
Basierend auf @Greg Hewgill, berücksichtigt jedoch negative Zahlen und gibt die Zeichenfolgengröße zurück.
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);
}
Nur ein FYI: das ‘Tausendertrennzeichen’-Flag für die
printf()
Familie formatierter IO-Funktionen (das einfache Anführungszeichen: ‘) ist ein nicht standardmäßiges Flag, das nur in einigen wenigen Bibliotheksimplementierungen unterstützt wird. Schade das es nicht Standard ist.– Michael Burr
20. September 2009 um 0:55 Uhr
Es ist vom Gebietsschema abhängig. Laut dem Linux-Manpagesieht es aus
LC_NUMERIC
. Ich weiß jedoch nicht, welches Gebietsschema dies unterstützt.– Joey Adams
4. November 2010 um 4:13 Uhr
@Joey, setze die
LC_NUMERIC
Gebietsschema zum Strom""
macht das'
funktioniert auf meinem Mac und auf einem Linux-Rechner, den ich gerade überprüft habe.– Karl Norum
27. Juli 2012 um 20:32 Uhr
Beachten Sie, dass die POSIX 2008 (2013)-Versionen der
printf()
Funktionsfamilie standardisiert die Verwendung der'
(einfaches Anführungszeichen oder Apostroph) mit den Konvertierungsspezifikationen für die Dezimalzahlenformatierung, um anzugeben, dass die Zahl mit Tausendertrennzeichen formatiert werden soll.– Jonathan Leffler
26. April 2015 um 18:09 Uhr
Beachten Sie dies auch in der Voreinstellung
"C"
Gebietsschema, das nicht-monetäre Tausendertrennzeichen ist undefiniert, also die"%'d"
erzeugt keine Kommas in der"C"
Gebietsschema. Sie müssen ein Gebietsschema mit einem geeigneten nicht-monetären Tausendertrennzeichen festlegen. Häufig,setlocale(LC_ALL, "");
wird die Aufgabe erledigen – andere Werte für den Gebietsschemanamen (außer der leeren Zeichenfolge) sind implementierungsdefiniert.– Jonathan Leffler
26. April 2015 um 18:46 Uhr