Шрифт:
Наконец, точность справа состоит из символа '
.
' и строки десятичных цифр. Она указывает, с каким числом значащих цифр округлить значение до форматирования. По умолчанию используются поля frac_digits
и int_frac_digits
в struct lconv
. Если это значение равно 0, десятичная точка не выводится. strfmon
возвращает число символов, помещенных в буфер, не включая завершающий нулевой байт. Если недостаточно места, функция возвращает -1 и устанавливает errno
в E2BIG
. Помимо
strfmon
, POSIX (но не ISO С) предусматривает специальный флаг — символ одинарной кавычки, '
— для форматов printf
%i
, %d
, %u
, %f
, %F
, %g
и %G
. В локалях, имеющих разделитель тысяч, этот флаг добавляет и его. Следующая простая программа, ch13-quoteflag.c
, демонстрирует вывод:
/* ch13-quoteflag.c --- демонстрация флага кавычки printf */
#include <stdio.h>
#include <locale.h>
int main(void) {
setlocale(LC_ALL, ""); /* Это нужно, иначе не будет работать */
printf("%'d\n", 1234567);
return 0;
}
Вот что происходит для двух различных локалей: в одной есть разделитель тысяч, в другой нет:
$ LC_ALL=C ch13-quoteflag /* Обычное окружение без разделителя */
1234567
$ LC_ALL=en_US ch13-quoteflag /* Локаль с разделителем (англ.) */
1,234,567
На время написания лишь GNU/Linux и Solaris поддерживают флаг
'
. Дважды проверьте справочную страницу printf(3) на своей системе. 13.2.6. Пример: форматирование числовых значений в
gawk
gawk
реализует свои собственные версии функций printf
и sprintf
. Для полного использования локали gawk
должен поддерживать флаг '
, как в С. Следующий фрагмент из файла builtin.c
в gawk
3.1.4 показывает, как gawk
использует struct lconv
для числового форматирования:
1 case 'd':
2 case 'i':
3 ...
4 tmpval = force_number(arg);
5
6 ...
7 uval = (uintmax_t)tmpval;
8 ...
9 ii = jj = 0;
10 do {
11 *--cp = (char)('0' + uval % 10);
12 #ifdef HAVE_LOCALE_H
13 if (quote_flag && loc.grouping[ii] && ++jj == loc.grouping[ii]) {
14 *--cp = loc.thousands_sep[0]; /* XXX - предположение, что это один символ */
15 if (loc.grouping[ii+1] == 0)
16 jj = 0; /* продолжить использовать текущий val в loc.grouping [ii] */
17 else if (loc.grouping[ii+1] == CHAR_MAX)
18 quote_flag = FALSE;
19 else {
20 ii++;
21 jj = 0;
22 }
23 }
24 #endif
25 uval /= 10;
26 } while (uval > 0);
(Номера строк даны относительно начала фрагмента.) Некоторые части кода, не имеющие отношения к обсуждению, были опущены, чтобы облегчить фокусировку на важных частях.
Переменная
loc
, используемая в строках 13–17, представляет struct lconv
. Она инициализируется в main
. Здесь для нас интерес представляет loc.thousands_sep
, который является символом разделителя тысяч, и loc.grouping
, который является массивом, описывающим число цифр между разделителями. Нулевой элемент означает «использовать для всех последующих цифр значение предыдущего элемента», а значение CHAR_MAX
означает «прекратить вставку разделителей тысяч». С таким введением, давайте посмотрим на код. Строка 7 устанавливает
uval
, которая является беззнаковой версией форматируемого значения. ii
и jj
отслеживают положение в loc.grouping
и число цифр в текущей группе, которые были преобразованы, соответственно [142] . quote_flag
равен true, когда в спецификации преобразования был отмечен символ '
. Цикл
do-while
генерирует символы цифр в обратном порядке, заполняя буфер с конца к началу. Каждая цифра создается в строке 11. Затем строка 25 делится на 10 путем смещения значения вправо на одну десятичную цифру.142
Нам, вероятно, следовало выбрать более осмысленные имена вместо простых
ii
и jj
, поскольку использующий их код короткий, отсутствие у нас воображения не представляет значительной проблемы — Примеч. автора.