Подтвердить что ты не робот

Как отформатировать номер от 1123456789 до 1 123 456 789 в C?

Как я могу на языке C указать число от 1123456789 до 1,123,456,789? Я попытался использовать printf("%'10d\n", 1123456789);, но это не сработает.

Не могли бы вы что-нибудь посоветовать? Чем проще решение, тем лучше.

4b9b3361

Ответ 1

Если ваш printf поддерживает флаг ' (как этого требует POSIX 2008 printf()), вы, вероятно, можете сделать это, просто установив соответствующий язык. Пример:

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

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

И построить и запустить:

$ ./example 
1,123,456,789

Протестировано на Mac OS X и Linux (Ubuntu 10.10).

Ответ 2

Вы можете сделать это рекурсивно следующим образом (остерегайтесь INT_MIN, если вы используете два дополнения, вам понадобится дополнительный код для управления этим):

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

Сумма:

  • Пользователь вызывает printfcomma с целым числом, специальный случай отрицательных чисел обрабатывается просто печатью "-" и делает число положительным (это бит, который не будет работать с INT_MIN).
  • Когда вы введете printfcomma2, число, меньшее 1000, будет просто печататься и возвращаться.
  • В противном случае рекурсия будет вызываться на следующем уровне вверх (так что 1,234,567 будет вызываться с 1,234, затем 1), пока не будет найдено число меньше 1.
  • Затем это число будет напечатано, и мы вернемся к дереву рекурсии, печатаем запятую и следующее число, когда мы идем.

Существует также более сжатая версия, хотя она делает ненужную обработку при проверке отрицательных чисел на каждом уровне (не то, что это имеет значение, учитывая ограниченное количество уровней рекурсии). Это полная программа для тестирования:

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

а выход:

-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

Итеративное решение для тех, кто не доверяет рекурсии (хотя единственная проблема с рекурсией имеет тенденцию быть стековым пространством, которое здесь не будет проблемой, поскольку оно будет всего лишь на несколько уровней даже для 64-битного целого числа ):

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

Оба из них генерируют 2,147,483,647 для INT_MAX.

Ответ 3

Здесь очень простая реализация. Эта функция не содержит проверки ошибок, размер буфера должен быть проверен вызывающим. Он также не работает для отрицательных чисел. Такие улучшения оставляются в качестве упражнения для читателя.

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

Ответ 4

Egads! Я делаю это все время, используя gcc/g++ и glibc на linux и да, "оператор может быть нестандартным, но мне нравится его простота".

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

int main()
{
    int bignum=12345678;

    setlocale(LC_ALL,"");

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

    return 0;
}

Дает вывод:

Большое число: 12 345 678

Просто нужно запомнить вызов "setlocale", иначе он ничего не форматирует.

Ответ 5

Возможно, будет интересна версия с поддержкой языка.

#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

У этого есть ошибка (но я считаю довольно незначительной). На двух дополнительных аппаратных средствах он не будет правильно преобразовывать наиболее отрицательное число, поскольку он пытается преобразовать отрицательное число в его эквивалентное положительное число с помощью N = -N;. В двух дополнениях максимально отрицательное число не имеет соответствующего положительного числа, если вы не продвинете его до более крупного типа. Один из способов обойти это - продвигать номер соответствующего неподписанного типа (но он несколько нетривиальный).

Ответ 6

Без рекурсии или обработки строк математический подход:

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

Подобно, в принципе, рекурсивному решению Pax, но, предварительно рассчитывая порядок величины, можно избежать рекурсии (возможно, при значительном расходе).

Обратите внимание также, что фактический символ, используемый для разделения тысяч, зависит от локали.

Изменить. См. комментарии @Chux ниже для улучшения.

Ответ 7

Основано на @Greg Hewgill's, но учитывает отрицательные числа и возвращает размер строки.

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

Ответ 8

Другая итеративная функция

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

Ответ 9

другое решение, сохраняя результат в массив int, максимальный размер, если 7 из-за длинного длинного int может обрабатывать число в диапазоне 9,223,372,036,854,775,807 до -9,223,372,036,854,775,807 note it is not an unsigned

функция нерекурсивной печати

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

вызов основной функции

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
}

тестовый выход

-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

в классе main()

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

если печать необходима, переместите int numberSeperated[8]; внутри функции getNumWcommas и назовите ее так getNumWcommas(number);

Ответ 10

Вот небольшая, размерная и быстродействующая реализация такого рода десятичной дроби:

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

Используйте следующее:

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

Вывод:

test : +1,234,567,890.

Некоторые преимущества:

  • Функция, принимающая конец буфера строк из-за обратного упорядоченного форматирования. Наконец, где нет необходимости в почитании сгенерированной строки (strrev).

  • Эта функция создает одну строку, которая может быть использована в любом альго после. Это не зависит и не требует нескольких вызовов printf/sprintf, которые ужасно медленны и всегда зависят от контекста.

  • Минимальное число операторов деления (/,%).

Ответ 11

Мой ответ не форматирует результат точно так же, как иллюстрация в вопросе, но может удовлетворить реальную потребность в некоторых случаях простым однострочным или макроснимком. Его можно расширить, чтобы при необходимости генерировать более тысячи групп.

Результат будет выглядеть следующим образом:

Value: 0'000'012'345

Код:

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

Ответ 12

Защищенные format_commas с отрицательными номерами:

Поскольку VS < 2015 не реализует snprintf, вам нужно сделать это

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

И затем

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

Пример использования:

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

Ответ 13

Я новичок в программировании на С. Вот мой простой код.

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

Ответ 14

Нет никакого реального простого способа сделать это в C. Я бы просто изменил функцию int-to-string, чтобы сделать это:

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 easier that way).
    // I'll let you figure that one out.
    strrev(out);
}

Ответ 15

Измененная версия решения @paxdiablo, но с использованием WCHAR и 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);
}

Ответ 16

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

В моем решении используется a. вместо того, Это позволяет читателю изменить это.

Ответ 17

Можно сделать довольно легко...

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

Пример вызова:

char szBuffer[512];
pretty_number("1234567", szBuffer);
//strcmp(szBuffer, "1,234,567") == 0

Ответ 18

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

Ответ 19

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(",");
           }

}