Как я могу в формате языка C ввести число от 1123456789
до 1,123,456,789
? Я пробовал использовать, printf("%'10d\n", 1123456789);
но это не сработало.
Не могли бы вы что-нибудь посоветовать? Чем проще решение, тем лучше.
c
formatting
numbers
иди
источник
источник
printf()
семейства форматированных функций ввода-вывода (символ одинарной кавычки: ') является нестандартным флагом, который поддерживается только в нескольких реализациях библиотеки. Жалко, что это нестандартно.LC_NUMERIC
. Однако я не знаю, какой регион поддерживает это.LC_NUMERIC
языкового стандарта на текущий""
делает'
работу на моем Mac и на Linux-машине, которую я только что проверил.printf()
семейства функций POSIX 2008 (2013) действительно стандартизируют использование'
символа (одинарная кавычка или апостроф) со спецификациями преобразования форматирования десятичных чисел, чтобы указать, что число должно быть отформатировано с использованием разделителей тысяч."C"
локали по умолчанию неденежный разделитель тысяч не определен, поэтому"%'d"
запятые в"C"
локали не будут. Вам необходимо установить языковой стандарт с соответствующим неденежным разделителем тысяч. Частоsetlocale(LC_ALL, "");
свою работу выполняет - другие значения имени локали (кроме пустой строки) определяются реализацией.Ответы:
Если ваш printf поддерживает этот
'
флаг (как того требует POSIX 2008printf()
), вы, вероятно, сможете сделать это, просто установив соответствующий языковой стандарт. Пример:#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).
источник
sprintf()
во встраиваемой системе, и он не работает (очевидно, потому что, как вы говорите, он не поддерживает флаг ''.'
модификатор. Из шапки:Copyright ... 2007 Joerg Wunsch ... 1993 Regents of the University of California
т.е. производная от BSD.Вы можете сделать это рекурсивно следующим образом (будьте осторожны,
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 будет просто напечатано и возвращено.Существует также более сжатая версия, хотя она выполняет ненужную обработку при проверке отрицательных чисел на каждом уровне (не то чтобы это будет иметь значение, учитывая ограниченное количество уровней рекурсии). Это полная программа для тестирования:
#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
.Весь приведенный выше код предназначен для трехзначных групп, разделенных запятыми, но вы также можете использовать другие символы, например пробел:
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); }
источник
n
вprintfcomma
. Вам нужно принудительно выполнить преобразование в беззнаковый, прежде чем отрицать его.printf
.Вот очень простая реализация. Эта функция не содержит не проверки ошибок, буферная размеры должны быть проверены абонентом. Это также не работает для отрицательных чисел. Такие улучшения оставлены в качестве упражнения для читателя.
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; }
источник
Эгадс! Я делаю это все время, используя 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, иначе он ничего не форматирует.
источник
'
флаг, вы не получите желаемого результата - и это не зависит от компилятора. Компилятор гарантирует, что библиотечная функцияprintf()
вызывается со строкой формата; интерпретировать это зависит от библиотечной функции. В Windows вполне возможно, что библиотека CRT не обеспечивает необходимой поддержки - и не имеет значения, какой компилятор вы используете.Возможно, будет интересна версия с учетом локали.
#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;
помощью дополнения до двух, максимально отрицательное число не имеет соответствующего положительного числа, если вы продвигайте его к большему шрифту. Один из способов обойти это - присвоить числу соответствующий беззнаковый тип (но это несколько нетривиально).источник
'
-flag здесь: stackoverflow.com/q/44523855/2642059 Я думаю, что этот ответ идеально подходит для этого, проводя больше тестов. Если так, я думаю, мне следует отметить этот вопрос как обман, да?tsep
,place_str
иneg_str
вообще? Почему бы просто не использоватьfmt_info
участников напрямую ?while (*ptr-- = *neg_str++)
для меня это не имеет большого смысла. Вы вставляете отрицательные строковые символы в обратном порядке.Математический подход без рекурсии или обработки строк:
#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 ниже для улучшений.
источник
abs(n)
наfabs(n)
предотвращает ошибку комплимента 2 при выполненииprint_number(INT_MIN)
.log10(abs(n))
а не где-либо еще. Интересно, что ваше решение работает с единственным изменением вlog10(fabs(n))
иprint_number(INT_MIN)
из-заprintf(..., abs(n / order_of_magnitude))
которой средствоn = abs(INT_MIN) % order_of_magnitude
отрицательны КИ. Если мы откажемся от INT_MIN, тоprintf(..., abs(n / order_of_magnitude))
может статьprintf(..., n / order_of_magnitude)
. Но я полагаю, что работать с червем под названием «abs (INT_MIN)» обычно плохо .log10(fabs(n))
,n = abs(n% order_of_magnitude)
иprintf(",%03d", n/order_of_magnitude)
. BTW: Я бы не стал тратить эти усилия, если не думал, что ваше решение было хорошим. Нет UB даже для INT_MIN.На основе @Greg Hewgill, но учитывает отрицательные числа и возвращает размер строки.
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); }
источник
Мой ответ форматирует результат не так, как на иллюстрации в вопросе, но может удовлетворить реальную потребность. в некоторых случаях с помощью простого однострочника или макроса. При необходимости его можно расширить, чтобы генерировать больше тысяч групп.
Результат будет выглядеть, например, следующим образом:
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);
источник
'
Эквивалентны ли стандартные обозначения a,
(по крайней мере математически) в какой-то части (ах) мира?В 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's easier that way). // I'll let you figure that one out. strrev(out); }
источник
Другая итерационная функция
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); } }
источник
Вот самая тонкая, эффективная по размеру и скорости реализация такого формата десятичных цифр:
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, что ужасно медленно и всегда зависит от контекста.
источник
unlikely
?unlikely
вероятно, намек оптимизатору на то, что условие вряд ли будет истинным. См.likely()
/unlikely()
Macros в ядре Linux для получения дополнительной информации.Безопасные форматные_запятые с отрицательными числами:
Поскольку 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);
источник
Модифицированная версия решения @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); }
источник
Я новичок в программировании на C. Вот мой простой код.
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 ... }
В моем решении используется. вместо a, читатель может это изменить.
источник
Это устарело, и на него есть множество ответов, но вопрос был не в том, «как я могу написать процедуру для добавления запятых», а в том, «как это можно сделать на C»? Комментарии указывали на это направление, но в моей системе Linux с GCC это работает для меня:
#include <stdio.h> #include <stdlib.h> #include <locale.h> int main() { unsetenv("LC_ALL"); setlocale(LC_NUMERIC, ""); printf("%'lld\n", 3141592653589); }
Когда это запущено, я получаю:
$ cc -g comma.c -o comma && ./comma 3,141,592,653,589
Если я отключу
LC_ALL
переменную перед запуском программы, вunsetenv
этом нет необходимости.источник
Мне нужно было сделать что-то подобное, но вместо прямой печати нужно было перейти в буфер. Вот что я придумал. Работает в обратном направлении.
unsigned int IntegerToCommaString(char *String, unsigned long long Integer) { unsigned int Digits = 0, Offset, Loop; unsigned long long Copy = Integer; do { Digits++; Copy /= 10; } while (Copy); Digits = Offset = ((Digits - 1) / 3) + Digits; String[Offset--] = '\0'; Copy = Integer; Loop = 0; do { String[Offset] = '0' + (Copy % 10); if (!Offset--) break; if (Loop++ % 3 == 2) String[Offset--] = ','; Copy /= 10; } while (1); return Digits; }
Имейте в виду, что он предназначен только для целых чисел без знака, и вы должны убедиться, что буфер достаточно большой.
источник
Другое решение - сохранить результат в
int
массиве с максимальным размером 7, посколькуlong long int
тип может обрабатывать числа в диапазоне от 9 223 372 036 854 775 807 до -9 223 372 036 854 775 807 . (Обратите внимание, что это не беззнаковое значение).Функция нерекурсивной печати
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 numberSeparated[8]; long long int number = 1234567890LL; getNumWcommas(number, numberSeparated);
Если печать - это все, что нужно, перейдите
int numberSeparated[8];
внутрь функцииgetNumWcommas
и вызовите ее таким образомgetNumWcommas(number)
.источник
Это делается довольно легко ...
//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
источник
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(","); } }
источник
,
для чисел ниже100
, использует,printf()
кудаputchar()
летит, использует вводящие в заблуждение имена, хаотичные отступы и слишком много кода.// 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'; }
источник
idx
может уйти. Код ничего не производит для 0. Он не обрабатывает отрицательные числа. Там нет очевидной причины , чтобыbuffer
вstatic
переменном (она ограничивает Реентерабельность кода). Нет объяснения того, что он делает, или упоминания о том, что после завершения кода строка, на которую указывает,p
содержит отформатированную строку. Наименее серьезная проблема заключается в том, что в качестве разделителя тысяч используется пробел вместо запятой. Однако тот факт, что он не обрабатывает ноль, - это проблема убийцы.