104 Stimmen

Wie formatiert man eine Zahl von 1123456789 bis 1.123.456.789 in C?

Wie kann ich in der Sprache C eine Zahl von 1123456789 a 1,123,456,789 ? Ich habe versucht, mit printf("%'10d\n", 1123456789); aber das funktioniert nicht.

Können Sie etwas empfehlen? Je einfacher die Lösung ist, desto besser.

101voto

Carl Norum Punkte 210051

Wenn Ihr printf die Option ' Flagge (wie von POSIX 2008 gefordert) printf() ), können Sie es wahrscheinlich einfach 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 und laufen lassen:

$ ./example 
1,123,456,789

Getestet unter Mac OS X und Linux (Ubuntu 10.10).

52voto

paxdiablo Punkte 809679

Sie können dies wie folgt rekursiv tun (Vorsicht 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);
}

Ein Resümee:

  • Benutzeraufrufe printfcomma mit einer ganzen Zahl wird der spezielle Fall negativer Zahlen behandelt, indem einfach "-" gedruckt und die Zahl positiv gemacht wird (das ist der Teil, der nicht mit INT_MIN ).
  • Wenn Sie eingeben printfcomma2 Eine Zahl unter 1.000 wird einfach gedruckt und zurückgegeben.
  • Andernfalls wird die Rekursion auf der nächsthöheren Ebene aufgerufen (1.234.567 wird also mit 1.234, dann 1 aufgerufen), bis eine Zahl kleiner als 1.000 gefunden wird.
  • Dann wird diese Zahl ausgedruckt, und wir gehen den Rekursionsbaum zurück, wobei wir ein Komma und die nächste Zahl ausgeben, während wir gehen.

Es gibt auch die knappere Version, die allerdings unnötige Verarbeitung bei der Prüfung auf negative Zahlen bei jede Ebene (nicht dass dies angesichts der begrenzten Anzahl von Rekursionsebenen eine Rolle spielen würde). 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 trauen (obwohl das einzige Problem bei der Rekursion der Stapelplatz ist, der hier kein Problem darstellt, da er selbst bei einer 64-Bit-Ganzzahl nur ein paar 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);
    }
}

Beide erzeugen 2,147,483,647 para INT_MAX .


Der gesamte obige Code ist für durch Komma getrennte dreistellige Gruppen gedacht, 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);
}

12voto

Greg Hewgill Punkte 882617

Hier ist eine sehr einfache Umsetzung. Diese Funktion enthält keine Fehlerprüfung, Puffergrößen müssen vom Aufrufer überprüft werden. Sie funktioniert auch nicht bei negativen Zahlen. Solche Verbesserungen bleiben 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;
}

8voto

lornix Punkte 1868

Oh je! Ich tue dies die ganze Zeit, mit gcc/g++ und glibc auf Linux und ja, der ' Operator kann nicht standardisiert sein, aber ich mag die Einfachheit der es.

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

int main()
{
    int bignum=12345678;

    setlocale(LC_ALL,"");

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

    return 0;
}

Ergibt eine Ausgabe von:

Große Zahl: 12.345.678

Sie müssen sich nur an den Aufruf "setlocale" erinnern, sonst wird nichts formatiert.

3voto

Jerry Coffin Punkte 452852

Vielleicht wäre eine ortsabhängige 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

Es gibt einen Fehler (den ich aber als relativ geringfügig ansehen würde). Auf Zweierkomplement-Hardware wird die negativste Zahl nicht korrekt konvertiert, weil versucht wird, eine negative Zahl in ihre äquivalente positive Zahl zu konvertieren mit N = -N; Im Zweierkomplement hat die maximal negative Zahl keine entsprechende positive Zahl, es sei denn, man erhöht sie auf einen größeren Typ. Eine Möglichkeit, dies zu umgehen, besteht darin, die Zahl in den entsprechenden vorzeichenlosen Typ zu überführen (dies ist jedoch nicht ganz trivial).

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X