Использование функции sprintf для вывода денежных сумм

Функция sprintf часто применяется для форматирования чисел с определенным количеством знаков в дробной части. Например, денежные суммы должны выводиться в виде 2.50, но не 2.5 – и конечно, не 2.49997! Задача легко решается при помощи формата "%.2f":

my $money = sprintf "%.2f", 2.49997;

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

sub big_money {
my $number = sprintf "%.2f", shift @_;
# При каждой итерации цикла добавляется одна запятая
1 while $number =~ s/^(-?\d+)(\d\d\d)/$1,$2/;
# Добавляем знак доллара в нужную позицию
$number =~ s/^(-?)/$1\$/;
$number;
}

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


Иначе говоря, если параметр содержит число 12345678.9, в $number сохраняется строка "12345678.90". В следующей строке кода используется модификатор while:

while ($number =~ s/^(-?\d+)(\d\d\d)/$1,$2/) {
1;
}

Что здесь происходит? Тело цикла выполняется, пока замена возвращает истинное значение (признак успешного выполнения). Но тело цикла не делает ничего! Для Perl это вполне допустимо, а нам эта запись сообщает, что целью этой команды является выполнение условия (замена), а не бесполезное тело цикла. Значение 1 традиционно используется как условный заполнитель, хотя подойдет и любое другое значение. Следующая запись работает точно так же, как и приведенный ранее цикл:

'keep looping' while $number =~ s/^(-?\d+)(\d\d\d)/$1,$2/;

Итак, теперь мы знаем, что цикл создан для выполнения замены. Но что делает замена? Напоминаем, что $number на этой стадии содержит строку вида "12345678.90". Шаблон совпадает с первой частью строки, но не может пройти дальше точки. (А вы видите, почему не может?) В переменную $1 заносится значение "12345", а в переменную $2 – значение "678", так что замена преобразует $number в "12345,678.90" (еще раз: совпадение для точки не находится, поэтому завершающая часть строки остается без изменений).


А вы видите, что делает дефис в начале шаблона? (Подсказка: дефис разрешен только в одном месте строки.) Если не догадаетесь сами, мы расскажем в конце раздела. Однако мы еще не закончили с командой замены. Так как замена завершилась успешно, фиктивный цикл возвращается для новой итерации. На этот раз возможные совпадения шаблона должны находиться до запятой, поэтому $number преобразуется в "12,345,678.90". Таким образом, замена включает запятую в число при каждой итерации цикла. Но цикл еще не завершен.

Предыдущая попытка замены была успешной, поэтому происходит очередная итерация цикла. Но на этот раз шаблон не совпадает, потому что в начале строки должно быть не менее четырех цифр; на этом цикл завершается. Почему мы не могли воспользоваться модификатором /g, чтобы выполнить глобальный поиск с заменой и избавиться от хлопот с 1 while? Это невозможно, так как мы перемещаемся назад от точки, а не вперед от начала строки. Расставить запятые в таких числах одной лишь подстановкой s///g невозможно. Кстати, вы поняли, зачем нужен дефис? Знак «-» в начале строки – это необязательный префикс. Он присутствует и в следующей строке, где знак $ выводится в нужной позиции, так что $number принимает вид "$12,345,678.90" (или, возможно, "–$12,345,678.90", если число отрицательное). Обратите внимание: знак доллара не всегда является первым символом в строке (хотя это бы значительно упростило ее). Наконец, последняя строка возвращает отформатированную «денежную сумму» для вывода в ежегодном отчете.

Оцените статью: (0 голосов)
0 5 0

Статьи из раздела Perl на эту тему:
Операции с подстроками и функция substr
Поиск подстроки по индексу
Расширенная сортировка
Сортировка по нескольким ключам
Сортировка хеша по значениям

Вернуться в раздел: Perl / 13. Строки и сортировка