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.
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).
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é:
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
).printfcomma2
, un nombre inférieur à 1 000 sera simplement imprimé et renvoyé.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
.
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;
}
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.
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).
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.
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);
}
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);
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.
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);
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);
}
}
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);
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);
}
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;
}
#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.
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);
}