Як я можу в мові 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.
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 цілком можливо, що бібліотека ЕПТ не надає потрібної вам підтримки - і не має значення, який компілятор ви використовуєте.
Можливо, цікавою є версія, яка відповідає локалі.
#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) ) ;
}
}
В принципі схоже на рекурсивне рішення Пакса, але заздалегідь обчислюючи порядок величини, рекурсії вдається уникнути (можливо, з деякими значними витратами).
Зауважте також, що фактичний символ, що використовується для розділення тисяч, залежить від мови.
Редагувати : Див. Коментарі @ 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);
'еквівалентно стандартне позначення ,(принаймні математично) в певних частинах світу?
Немає реального простого способу зробити це в 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()макроси в ядрі 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 076 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()сімейства відформатованих функцій вводу-виводу (символ із одинарними лапками: ') - це нестандартний прапор, який підтримується лише в декількох реалізаціях бібліотеки. Шкода, що це не стандартно.