Как я могу в формате языка C ввести число от 1123456789
до 1,123,456,789
? Я пробовал использовать, printf("%'10d\n", 1123456789);
но это не сработало.
Не могли бы вы что-нибудь посоветовать? Чем проще решение, тем лучше.
Как я могу в формате языка C ввести число от 1123456789
до 1,123,456,789
? Я пробовал использовать, printf("%'10d\n", 1123456789);
но это не сработало.
Не могли бы вы что-нибудь посоветовать? Чем проще решение, тем лучше.
LC_NUMERIC
. Однако я не знаю, какой регион поддерживает это.
LC_NUMERIC
языкового стандарта на текущий ""
делает '
работу на моем Mac и на Linux-машине, которую я только что проверил.
printf()
семейства функций POSIX 2008 (2013) действительно стандартизируют использование '
символа (одинарная кавычка или апостроф) со спецификациями преобразования форматирования десятичных чисел, чтобы указать, что число должно быть отформатировано с использованием разделителей тысяч.
"C"
локали по умолчанию неденежный разделитель тысяч не определен, поэтому "%'d"
запятые в "C"
локали не будут. Вам необходимо установить языковой стандарт с соответствующим неденежным разделителем тысяч. Часто setlocale(LC_ALL, "");
свою работу выполняет - другие значения имени локали (кроме пустой строки) определяются реализацией.
Ответы:
Если ваш 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).
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
содержит отформатированную строку. Наименее серьезная проблема заключается в том, что в качестве разделителя тысяч используется пробел вместо запятой. Однако тот факт, что он не обрабатывает ноль, - это проблема убийцы.
printf()
семейства форматированных функций ввода-вывода (символ одинарной кавычки: ') является нестандартным флагом, который поддерживается только в нескольких реализациях библиотеки. Жалко, что это нестандартно.