[Предполагаемый заголовок: Пусть это выглядит так, как вы захотите]
Перевод Anton Petrusevich <casus@mail.ru> и Alex Ott <ott@phtd.tpu.edu.ru>
В большинстве случаев, программисты на Perl для вывода данных
стараются использовать великолепный print
или время от времени
окунаются в мир форматов, для того, чтобы быстро сделать наброски
различных отчетов. Однако, оператор printf
, на который часто
не обращают внимания, обеспечивает необходимое количество параметров
настройки, для того, чтобы строки выглядели так, как вы этого хотите.
Оператор printf
получает строку формата и ноль или
несколько значений. Строка формата управляет всем процессом. За
несколькими исключениями, каждое поле со знаком процента %
используемое в строке формата, соответствует одному из дополнительных
значений, определяя то, как значение будет выглядеть при
выводе. Например:
printf "my string %s has %d characters.\n", $str, length($str);
Здесь поле %s
выдает символьное значение, которое находится
в переменной $str
. Аналогичным образом поле %d
выдает
десятичное значение, вычисленное операцией
length($str)
. Параметры оцениваются в списочном контексте, так
что мы могли бы использовать следующий код для достижения тех же
результатов:
@output = ($str, length($str)); printf "my string %s has %d characters.\n", @output;
Задача становится интересной, если мы не знаем длину массива
@output
, поскольку нам необходимо иметь поле %
для
каждого из элементов массива @output
, но поскольку мы сами
создали массив, то здесь нет никаких проблем.
Кроме %s
для строк и %d
для десятичных целых
чисел, другим часто применяемым форматом является %f
для чисел
с плавающей запятой:
printf "he has $%f in his account\n", 3.50;
Здесь значение 3.5 выдается как число с плавающей точкой
3.500000
. Но почему появились лишние нули? По умолчанию
точность для чисел с плавающей запятой равна 6 символам после
десятичной точки. Для уменьшения этого значения, мы можем управление
точностью в формат, поместив нужное число между %
и
f
.
printf "he has $%.2f in his account\n", 3.50;
И это выдаст нам 3.50
, как мы и ожидали. Здесь 2
означает, что будет выводиться дву цифры, что станет означать
центы. Для того, чтобы вместиться в заданный формат, число округляется,
так что 3.509
должно показываться как 3.51
, а
3.502
будет выведено как 3.50
. В качестве крайнего
случая мы можем использовать %.0f
для округления до ближайшего
целого числа, и десятичная точка не будет использоваться.
Другим общим форматом в научной нотации является %e
. Он
полезен того, когда число может быть слишком большим для представления
несколькими цифрами:
printf "2 to the 100 power is approximately %e\n", 2 ** 100;
Это выдаст нам 1.267651e+30
, снова используя 6 цифр после
десятичной точки, до тех пор пока мы не будем явно задавать точность,
например как %.10e
.
Но %e
редко используется (как я заметил). В общем случае,
когда выдается число неизвестной величины или точности, то большинство
программистов возвращаются к использованию %g
``общему формату
чисел''. В этом случае, число форматируется используя либо %d
,
либо %f
, либо %e
, в зависимости от того, какой из
форматов дает ``лучший'' результат. Если число является целым, то будет
использован формат для целых чисел. Если число с плавающей точкой имеет
разумную величины, то используется обычный формат, а в противном случае
будет использоваться научный вид записи чисел.
printf "Your number is %g\n", $number;
Снова может быть использовано указание точности, но в этом случае
это поле указывает максимальное количество значащих цифр, со значением
по умолчанию равным 6. Так что для %.15g
, мы получим наилучшее
отображение 15 наиболее значащих цифр.
Для строк, мы получаем аналогичный контроль ``точности''. Если мы включаем точности для строки, а строка длинее заданного числа, то она автоматически сокращается:
printf "I said %.5s!\n", "hello world";
что выдает I said hello!
, обрезая строку.
Другой возможностью printf
является указание ширины
поля. После того, как будет определено значение для отдельного поля, то
может использоваться минимальная ширина поля, указанная десятичным
числом после знака процента:
printf "=%10s=\n", "hello";
Здесь строка из символов не заполняет все 10 символов, так что слева будет добавлено 4 пробела. Здесь указывается минимальная, а не максимальная ширина поля, так что если строка будет длинее, то она будет выдана полностью. Мы можем объединить поле указания точности с полем указания ширины, для того, чтобы получить строку, которая дополнена до заданного размера, или обрезана, если строка превышает заданный размер. Рассмотрим простой код:
printf "=%5.5s=\n", substr("1234567890", 0, $_) for 0..10;
что выдает нам великолепную модель:
= = = 1= = 12= = 123= = 1234= =12345= =12345= =12345= =12345= =12345= =12345=
Дополнение пробелами может происходить справа, а не слева, при использовании отрицательного числа в качестве минимальной ширины:
printf "=%-5.5s=\n", substr("1234567890", 0, $_) for 0..10;
= = =1 = =12 = =123 = =1234 = =12345= =12345= =12345= =12345= =12345= =12345=
Также числа могут дополняться нулями, а не пробелами, если
использовать знак 0
в начале ширины:
printf "%02d:%02d:%02d %s", $h, $m, $s, $ampm;
Если число в $m
меньше чем 10 (например 7), то мы получим в
выводе ведущий 0 (например 07
), что очень полезно для отображения
времени, как в нашем случае.
Знак %
может быть получен его дублированием, как в коде:
printf "He scored %.0f%% of the goals", 100 * $him / $total;
Заметьте, что часто предпринимаемое маскирование %
с помощью
символа обратный слэш, не будет работать. Это не проблема интерполяции
строк: это проблема интерпретации в printf
.
Одним, из менее часто используемых форматов является ``символьный'' формат:
printf "the letter A is %c\n", 65;
Здесь, значение 65 рассматривается как код ASCII, и превращается в символ ``A''. Этот формат не так часто используется в Perl, в отличии от C, поскольку Perl связан со строками как типами данных первого класса, редко раскрывая программисту численные значения индивидуальных символов.
И имеются форматы ``типов для программиста'' ... %h
для
шестнадцатеричного вывода, %o
для восьмеричного, и новый
формат в Perl 5.6, %b
для двоичного вывода. Например, вот один
из способов для того, чтобы показать права доступа к файлу:
printf "%s is mode %o\n", $_, 07777 & (stat)[2] for @ARGV;
Но глядя на вывод, значения искажают все вокруг. Нужно использовать поле минимальной длины:
printf "%30s is mode %04o\n", $_, 07777 & (stat)[2] for @ARGV;
Это основы, но давайте также взглянем на некоторый практический
код.Предположим, что у меня есть набор значений в массиве
@numbers
, которые я хочу напечатать в виде вертикальной
колонки с форматом %15g
для всех чисел. Вы можете подумать,
что я могу просто сделать следующее:
printf "%15g\n", @numbers; # bad
Но этот код не будет работать, поскольку поле %
необходимо
для каждого из значений списка (как мы увидели ранее). Хорошо, простым
способом исправления этой проблемы является использовании цикла:
printf "%15g\n", $_ for @numbers;
Но другим способом является репликация строки формата. Если нам
необходимо выдать три записи, что нам нужна строка
%15g\n%15g\n%15g\n
, которую мы можем получить с помощью
операции "%15g\n" x 3
. Так что нам необходимо знать
число элементов в массиве @numbers
для использования в правой
части оператора x
. Это достаточно просто: просто используйте
имя массива в скалярном контексте (который здесь и используется!):
printf "%15g\n" x @numbers, @numbers;
Здесь массив @numbers
используется и в скалярном и в
списочном контексте в одном выражении: один и тот же текст, но с разным
значением.
Время от времени, вам может понадобиться переменная дина
колонки. Например вам может понадобиться, чтобы число 15
из
предыдущего примера было настраиваемым:
$width = 15; printf "%$widthg\n", $_ for @numbers; # bad
Этот код не работает, поскольку Perl ищет переменную с именем
$widthg
, даже если вы использовали имя переменной
$width
за которой следует g
. Но вы также не можете
помести здесь пробел, поскольку формат для printf
разборчив и
не понимает пробелов. Одним из решений является отделение имени
переменной:
$width = 15; printf "%${width}g\n", $_ for @numbers;
Другим способом является использование в списке *
для того,
чтобы определить число. Каждое появление *
в формате поля
заставляет использовать один элемент из списка значений для численного
значения, которое обозначает *
:
$width = 15; printf "%*g\n", $width, $_ for @numbers;
И мы получили это. Много способов для выдачи ваших чисел, строк и всего, что получается в ваших вычислениях. Встретимся в следующий раз, наслаждайтесь!