Как отформатировать число от 1123456789 до 1,123,456,789 в C?


83

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

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


1
Просто к сведению: флаг «разделитель тысяч» для printf()семейства форматированных функций ввода-вывода (символ одинарной кавычки: ') является нестандартным флагом, который поддерживается только в нескольких реализациях библиотеки. Жалко, что это нестандартно.
Майкл Берр,

1
Это зависит от региона. Согласно странице руководства Linux , он смотрит на LC_NUMERIC. Однако я не знаю, какой регион поддерживает это.
Джои Адамс

1
@Joey, установка LC_NUMERICязыкового стандарта на текущий ""делает 'работу на моем Mac и на Linux-машине, которую я только что проверил.
Carl Norum

Обратите внимание, что версии printf()семейства функций POSIX 2008 (2013) действительно стандартизируют использование 'символа (одинарная кавычка или апостроф) со спецификациями преобразования форматирования десятичных чисел, чтобы указать, что число должно быть отформатировано с использованием разделителей тысяч.
Джонатан Леффлер,

2
Также обратите внимание, что в "C"локали по умолчанию неденежный разделитель тысяч не определен, поэтому "%'d"запятые в "C"локали не будут. Вам необходимо установить языковой стандарт с соответствующим неденежным разделителем тысяч. Часто setlocale(LC_ALL, "");свою работу выполняет - другие значения имени локали (кроме пустой строки) определяются реализацией.
Джонатан Леффлер,

Ответы:


83

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

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

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


1
Я тестировал это sprintf()во встраиваемой системе, и он не работает (очевидно, потому что, как вы говорите, он не поддерживает флаг ''.
gbmhunter

Я уверен, что вы можете найти библиотеку C, которая без особых проблем поддерживала бы ее.
Карл Норум

Я быстро просмотрел, не нашел ничего подходящего и реализовал свое, используя некоторые из идей выше. Было бы здорово найти настоящую библиотеку, чтобы вы могли использовать ее для чисел с плавающей запятой и строк с десятичными знаками.
gbmhunter

1
К сожалению , встроенная система printf () в FWIW AtmelStudio не поддерживает 'модификатор. Из шапки: Copyright ... 2007 Joerg Wunsch ... 1993 Regents of the University of Californiaт.е. производная от BSD.
Боб Стейн,

2
Хотя это удобно - вы не обязательно хотите изменять состояние для этой функции (setlocale).
ideasman42 08

46

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

Резюме:

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

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

и вывод:


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

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


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


Я думаю, что это лучше решать итеративно, поскольку проблема более естественна итеративна («отделить каждую третью цифру»), чем рекурсивной («отделить третью цифру от остальных, а затем повторить это для остальных»).
Joren

Предлагаемое исправление для MIN_INT: измените printfcomma2 на беззнаковое int. Вот и все. Не очень много «лишнего кода» :-)
Стив Джессоп

@Joren: Я добавил итеративное решение, и в некоторой степени оно показывает, почему рекурсивное решение имеет достоинства. Хотя во многих случаях предотвращение рекурсии является проблемой стандартов кодирования.
Клиффорд

@Steve: Просто изменяя тип аргумента не исправить , потому что UB был вызван , как только вы отрицать nв printfcomma. Вам нужно принудительно выполнить преобразование в беззнаковый, прежде чем отрицать его.
R .. GitHub ПРЕКРАТИТЕ ПОМОЩЬ ICE

1
@Nehal, он не запускается снова в том смысле, что весь текущий прогресс утерян. Он вызывает себя рекурсивно, а затем возвращается к следующему оператору, оператору printf.
paxdiablo

11

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


Мне нравится этот, он использует sprintf вместо printf, что полезно для встроенных систем.
gbmhunter

1
Довольно неплохо, но для работы с отрицательными числами требуются незначительные настройки.
ideasman42

(модифицированная версия для поддержки отрицательных чисел stackoverflow.com/a/24795133/432509 )
ideasman42 08

5

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

Дает результат:

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

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


2
К сожалению, в Windows / gcc 4.9.2 это не работает.
rdtsc

Что ж, Драт! Я бы подумал, что gcc на любой платформе даст аналогичные результаты независимо от ОС. Хорошо знать, я думаю, интересно, почему. Хммммм .....
lornix

Обратите внимание: если используемая библиотека C не поддерживает этот 'флаг, вы не получите желаемого результата - и это не зависит от компилятора. Компилятор гарантирует, что библиотечная функция printf()вызывается со строкой формата; интерпретировать это зависит от библиотечной функции. В Windows вполне возможно, что библиотека CRT не обеспечивает необходимой поддержки - и не имеет значения, какой компилятор вы используете.
Джонатан Леффлер

3

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

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


Я задал вопрос, направленный больше на кроссплатформенную реализацию формата '-flag здесь: stackoverflow.com/q/44523855/2642059 Я думаю, что этот ответ идеально подходит для этого, проводя больше тестов. Если так, я думаю, мне следует отметить этот вопрос как обман, да?
Джонатан Ми

Хорошо, первое, что я заметил, это не регулируется при настройке локали. Зачем поддерживать, tsep, place_strи neg_strвообще? Почему бы просто не использовать fmt_infoучастников напрямую ?
Джонатан Ми

Хорошо, номер 2, этот код не может обрабатывать отрицательные числа ... и я точно не знаю, как это могло быть, while (*ptr-- = *neg_str++)для меня это не имеет большого смысла. Вы вставляете отрицательные строковые символы в обратном порядке.
Джонатан Ми

Итак ... Я устранил утечку памяти и исправил ошибку с отрицательными числами: ideone.com/gTv8Z4 К сожалению, все еще существует проблема с множественными разделителями символов или множественными отрицательными символами, записываемыми в строку в обратном направлении. Я попытаюсь решить это в следующий раз ...
Джонатан Ми,

@JonathanMee: Я обновил код (и добавил еще как минимум несколько тестовых примеров, включая отрицательные числа).
Джерри Коффин

2

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

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

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

Изменить : см. Комментарии @ Chux ниже для улучшений.


1
Изменение abs(n)на fabs(n)предотвращает ошибку комплимента 2 при выполнении print_number(INT_MIN).
chux

@chux: Хороший момент, но в выражении% LHS будет возвращен к int, и он все равно будет сломан. Возможно, проще просто принять немного меньший диапазон допустимого ввода или добавить тест и вывод «-2 147 483 647» непосредственно для INT_MIN (или любого другого 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)» обычно плохо .
chux - Восстановить Монику

Новая мысль: предложить 3 изменения log10(fabs(n)), n = abs(n% order_of_magnitude)и printf(",%03d", n/order_of_magnitude). BTW: Я бы не стал тратить эти усилия, если не думал, что ваше решение было хорошим. Нет UB даже для INT_MIN.
chux

2

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


1

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

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

Value: 0'000'012'345

Код:


'Эквивалентны ли стандартные обозначения a ,(по крайней мере математически) в какой-то части (ах) мира?
ysap 04

1
@ysap Это разделитель тысяч в некоторых частях мира.
Роланд

0

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


0

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


Меня заинтриговало выражение, используемое для определения размерности массива !? Есть ли этому математическое обоснование?
Клиффорд,

ld (10) бит для каждой десятичной цифры. Округляя до 3. мы можем снова разделить 3 (чтобы учесть тот факт, что мы храним до 3 цифр одновременно). Но я хотел держать его на верхнем пределе.
Йоханнес Шауб - лит,

0

Вот самая тонкая, эффективная по размеру и скорости реализация такого формата десятичных цифр:

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

Вывод:

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

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

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

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

Что есть unlikely?
Дэн

1
@Dan: unlikelyвероятно, намек оптимизатору на то, что условие вряд ли будет истинным. См. likely()/ unlikely()Macros в ядре Linux для получения дополнительной информации.
Джонатан Леффлер

@JonathanLeffler Ага. Спасибо за ссылку.
Дэн

0

Безопасные форматные_запятые с отрицательными числами:

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

А потом

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


0

Модифицированная версия решения @paxdiablo, но с использованием WCHARи wsprinf:


0

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


0

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


0

Это устарело, и на него есть множество ответов, но вопрос был не в том, «как я могу написать процедуру для добавления запятых», а в том, «как это можно сделать на C»? Комментарии указывали на это направление, но в моей системе Linux с GCC это работает для меня:

Когда это запущено, я получаю:

Если я отключу LC_ALLпеременную перед запуском программы, в unsetenvэтом нет необходимости.


0

Мне нужно было сделать что-то подобное, но вместо прямой печати нужно было перейти в буфер. Вот что я придумал. Работает в обратном направлении.

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


0

Другое решение - сохранить результат в intмассиве с максимальным размером 7, поскольку long long intтип может обрабатывать числа в диапазоне от 9 223 372 036 854 775 807 до -9 223 372 036 854 775 807 . (Обратите внимание, что это не беззнаковое значение).

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

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

результаты тестирования

В функции main ():

Если печать - это все, что нужно, перейдите int numberSeparated[8];внутрь функции getNumWcommasи вызовите ее таким образом getNumWcommas(number).


-1

Это делается довольно легко ...

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


-1

1
По крайней мере, используйте правильный отступ при публикации кода. Также, возможно, добавьте объяснение того, что это делает, чего еще не делают существующие ответы.
EWit

Это простота, и ее легко понять с первого взгляда.
Стив Ньюман

1
Фальшивое решение, выводит дополнительную цифру ,для чисел ниже 100, использует, printf()куда putchar()летит, использует вводящие в заблуждение имена, хаотичные отступы и слишком много кода.
chqrlie

-1

1
Этот код имеет множество проблем. Неиспользованная переменная idxможет уйти. Код ничего не производит для 0. Он не обрабатывает отрицательные числа. Там нет очевидной причины , чтобы bufferв staticпеременном (она ограничивает Реентерабельность кода). Нет объяснения того, что он делает, или упоминания о том, что после завершения кода строка, на которую указывает, pсодержит отформатированную строку. Наименее серьезная проблема заключается в том, что в качестве разделителя тысяч используется пробел вместо запятой. Однако тот факт, что он не обрабатывает ноль, - это проблема убийцы.
Джонатан Леффлер,
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.