Як я можу в мові 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()
сімейства відформатованих функцій вводу-виводу (символ із одинарними лапками: ') - це нестандартний прапор, який підтримується лише в декількох реалізаціях бібліотеки. Шкода, що це не стандартно.