web-dev-qa-db-fra.com

Comment formater un nombre de 1123456789 à 1 123 456 789 en C?

Comment puis-je, en langage C, formater un nombre de 1123456789 à 1,123,456,789? J'ai essayé d'utiliser printf("%'10d\n", 1123456789); mais cela ne fonctionne pas.

Pourriez-vous conseiller quelque chose? Plus la solution est simple, mieux c'est.

63
goe

Si votre printf prend en charge l'indicateur ', vous pouvez probablement le faire en définissant simplement les paramètres régionaux Exemple:

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

int main(void)
{
    setlocale(LC_NUMERIC, "");
    printf("%'d\n", 1123456789);
    return 0;
}

Et construire et exécuter:

$ ./example 
1,123,456,789

Testé sur Mac OS X et Linux (Ubuntu 10.10).

68
Carl Norum

Vous pouvez le faire récursivement comme suit (méfiez-vous INT_MIN si vous utilisez un complément à deux, vous aurez besoin de code supplémentaire pour gérer cela):

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

Un résumé:

  • Les appels des utilisateurs printfcomma avec un entier, le cas particulier des nombres négatifs sont gérés en imprimant simplement "-" et en rendant le nombre positif (c'est le bit qui ne fonctionnera pas avec INT_MIN).
  • Lorsque vous entrez printfcomma2, un nombre inférieur à 1 000 sera simplement imprimé et renvoyé.
  • Sinon, la récursivité sera appelée au niveau supérieur (ainsi, 1 234 567 seront appelés avec 1 234, puis 1) jusqu'à ce qu'un nombre inférieur à 1 000 soit trouvé.
  • Ensuite, ce numéro sera imprimé et nous remonterons dans l’arbre de récurrence en imprimant une virgule et le numéro suivant au fur et à mesure.

Il existe également la version la plus succincte, bien qu’elle effectue un traitement inutile en vérifiant les nombres négatifs à à tous les niveaux (ce qui n’est pas important compte tenu du nombre limité de niveaux de récursivité). Celui-ci est un programme complet pour tester:

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

et le résultat est:

-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

Une solution itérative pour ceux qui ne font pas confiance à la récursivité (bien que le seul problème avec la récursivité ait tendance à être l'espace de pile qui ne sera pas un problème ici car il ne sera profond que de quelques niveaux même pour un entier 64 bits)

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

Tous deux génèrent 2,147,483,647 pour INT_MAX.

38
paxdiablo

Voici une implémentation très simple. Cette fonction contient no vérification d'erreur, les tailles de mémoire tampon doivent être vérifiées par l'appelant. Cela ne fonctionne pas non plus pour les nombres négatifs. Ces améliorations sont laissées comme un exercice pour le lecteur.

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;
}
11
Greg Hewgill

Des œufs! Je le fais tout le temps, en utilisant gcc/g ++ et glibc sur linux et oui, l'opérateur 'peut être non standard, mais j'aime sa simplicité.

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

int main()
{
    int bignum=12345678;

    setlocale(LC_ALL,"");

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

    return 0;
}

Donne la sortie de:

Grand nombre: 12 345 678

Souvenez-vous simplement de l'appel 'setlocale', sinon il ne formatera rien.

6
lornix

Peut-être qu'une version tenant compte des paramètres régionaux serait intéressante.

#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

Cela a un bug (mais je considère que c'est assez mineur). Sur le complément à deux, le nombre le plus négatif ne sera pas converti correctement car il tente de convertir un nombre négatif en son nombre positif équivalent avec N = -N; Dans le complément à deux, le nombre maximal négatif ne correspond pas au nombre positif correspondant, sauf vous faites la promotion d'un type plus large. Une façon de contourner ce problème consiste à promouvoir le nombre avec le type non signé correspondant (mais ce n'est pas très trivial).

4
Jerry Coffin

Sans récursion ni traitement des chaînes, une approche mathématique:

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

Similaire en principe à la solution récursive de Pax, mais en calculant l'ordre de grandeur à l'avance, on évite la récursivité (à un coût considérable peut-être).

Notez également que le caractère utilisé pour séparer des milliers dépend des paramètres régionaux.

Edit : Voir les commentaires de @ Chux ci-dessous pour des améliorations.

3
Clifford

Basé sur @Greg Hewgill, mais prend en compte les nombres négatifs et renvoie la taille de la chaîne.

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

une autre solution, en enregistrant le résultat dans un tableau int, taille maximale si 7 en raison du long long int peut traiter un nombre compris entre 9.223.372.036.854.775.707 à -9.223.372.036.854.775.807 note it is not an unsigned

fonction d'impression non récursive

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

appel de fonction principale

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
}

test de sortie

-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

dans la classe main ()

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

si l'impression est nécessaire, déplacez int numberSeperated[8]; dans la fonction getNumWcommas et appelez-le de cette façon getNumWcommas(number);

1
aah134

Voici la mise en oeuvre la plus fine, la plus efficace en taille et en rapidité de ce type de formatage à chiffres décimaux:

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

Utiliser comme suit:

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

Sortie:

test : +1,234,567,890.

Quelques avantages:

  • Fonction prenant la fin du tampon de chaîne en raison d'un formatage inversé. Enfin, il n’est pas nécessaire de revenir sur la chaîne générée (strrev).

  • Cette fonction produit une chaîne qui peut être utilisée dans n’importe quel algo suivant. Cela ne dépend pas et n'exige pas plusieurs appels à printf/sprintf, ce qui est terriblement lent et toujours spécifique au contexte.

  • Nombre minimal d'opérateurs de division (/,%).
1
Zorg

Ma réponse ne formate pas le résultat exactement comme dans l'illustration de la question, mais peut répondre au besoin réel dans certains cas avec une simple ligne ou une macro. On peut l’étendre pour générer plus de mille groupes si nécessaire.

Le résultat ressemblera par exemple à ceci:

Value: 0'000'012'345

Le code:

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

Une autre fonction itérative

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

Format_commas sécurisé, avec des nombres négatifs: 

Comme VS <2015 n'implémente pas snprintf, vous devez le faire. 

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

Et alors

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

Exemple d'utilisation: 

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);
1
Stefan Steiger

Il n'y a pas de moyen simple de faire cela en C. Je voudrais simplement modifier une fonction de chaîne à chaîne pour le faire:

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);
}
0
Jeremy Ruten

Je suis nouveau dans la programmation en C. Voici mon code simple.

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;
}
0
K.tin
#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 ...
}

Ma solution utilise un. au lieu d'un, .__ Il est laissé au lecteur de changer cela.

0
Frank Abbing

Une version modifiée de la solution @paxdiablo, mais avec WCHAR et wsprinf:

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