Форматируем вывод чисел в Java
Всем привет! Часто в наши методы приходят числа, которые нужно отобразить в каком-то особом формате. Вроде бы как мелочь, но как бы вы реализовали эту задачу? Предлагаем над этим сегодня немного поразмыслить. Для начала, чтобы с головой окунуться в форматирование чисел в Java, давайте вспомним метод format класса String : public static String format(String format, Object… args) — возвращает строку, отформатированную из строки format с помощью остальных аргументов args . И сразу пример:
String str = String.format("Привет - %s! Как дела %s?", "Саша", "на работе"); System.out.println(str);
Методы printf и format
String.format() — не единственный метод для форматирования строки. Его аналогами могут служить System.out.printf() и System.out.format(); . Так, предыдущий код мы можем заменить на:
System.out.printf("Привет - %s! Как дела %s?", "Саша", "на работе");
System.out.format("Привет - %s! Как дела %s?", "Саша", "на работе");
Вывод в консоли при этом останется тем же. Единственным отличием служит только то, что данные методы сразу выводят значение в консоли, в отличие от String.format() . Но мне String.format() нравится больше, так как нам не всегда нужно выводить результат в консоли, поэтому далее мы будем использовать именно этот способ. Вернемся же к нашему примеру. Что мы видим? А то, что в места, где были символы — %s , вставлены строки — «Саша» и «на работе» . Каким образом это может нам помочь при разработке? Представьте, что у вас есть большой шаблонный текст, но в некоторых местах вам нужно вставлять значения, которые могут быть разными и приходить в качестве аргументов извне. Вот тут нам и пригодится данное форматирование. Спецификаторы формата начинаются со знака процента % и заканчиваются символом, указывающим тип аргумента, который нужно отформатировать. И, как вы наверное поняли, %s используется для вставки объектов — строк. Но если мы попробуем вставить, к примеру, double в место, в котором прописан объект строки:
String str = String.format("Привет - %s! Как дела %s?", 55.6, "на работе");
Помимо строк и чисел с плавающей запятой, в Java есть и другие типы, не так ли? Поэтому давайте взглянем на весь арсенал:
String.format("Число ПИ равно - %f!", 3.14159)
String.format("Число ПИ равно - %e!", 3.14159);
String.format("Число ПИ равно - %a!", 3.14159)
String.format("Сегодня %tA",new Date())
String.format(" Привет %n Привет")
String str = String.format("Расстояние от Киева до Одессы - %f. Не так уж и мало, не правда ли? ", 475.4d); System.out.println(str);
Как вы уже поняли, %f будет более подходящим спецификатором для чисел с плавающей запятой, которые включают в себя такие типы данных как double и float в Java. С этим спецификатором мы можем форматировать число с плавающей запятой:
String str = String.format("Расстояние от Киева до Одессы - %.2f. Не так уж и мало, не правда ли? ", 475.4d);
Вставка .2 в данный спецификатор обрежет количество знаков после запятой до двух, и мы получим вывод:
.2 — не единственная поднастройка спецификаторов. Комбинация данных поднастроек называется инструкцией .Общий вид инструкции такой:
- [аргумент_индекс] — целое число, указывающее позицию в списке аргументов. К примеру, ссылка на первый аргумент 1$, ссылка на второй аргумент — 2$, и т.д. Если же позиция не была задана, аргументы должны находиться в том же порядке, что и ссылки на них в строке форматирования.
- [флаги] — специальные символы для форматирования. Например:
- + флаг, означающий, что если числовое значение положительное, оно должно включать знак +
- — означает выравнивание результата по левому краю
- , устанавливает разделитель тысяч у целых чисел
String str = String.format("%1$+09.5f", 3.1415926535897); System.out.print(str);
Вроде несложно, так? Но когда заходит речь о форматировании числа, то нельзя обойти стороной DecimalFormat . Давайте разберемся, что имеется в виду.
DecimalFormat
DecimalFormat — класс для форматирования любого числа в Java, будь то целое число или число с плавающей запятой. Когда происходит создание объекта DecimalFormat , прямо в конструкторе можно задать шаблон форматирования приходящих чисел. Как будет выглядеть наш пример с использованием DecimalFormat :
DecimalFormat dF = new DecimalFormat( "#.###" ); double value = 72.224463; System.out.print(dF.format(value));
- # — цифра, ведущие нули опускаются;
- 0 — цифра отображается всегда, даже если в номере меньше цифр (в таком случае отображается 0);
- . — знак десятичного разделителя;
- , — знак группировки разделителей (например, разделитель тысяч);
- ; — разделяет форматы;
- — — отмечает префикс отрицательного числа;
- % — умножает на 100 и показывает число в процентах;
- ? — умножает на 1000 и показывает число в промилле;
- E — разделяет мантиссу и порядок для экспоненциального формата.
System.out.println(new DecimalFormat( "###,###.##" ).format(74554542.224463));
System.out.println(new DecimalFormat( "%###.##" ).format(0.723456));
System.out.println(new DecimalFormat( "000.###" ).format(42.224463));
Не обязательно создавать каждый раз новый объект DecimalFormat , чтобы задать новый шаблон. Будет достаточно использовать его методы applyPattern и applyLocalizedPattern :
DecimalFormat dF = new DecimalFormat("###.###"); dF.applyPattern("000000.000"); dF.applyLocalizedPattern("#,#00.0#");
Когда мы говорим о форматировании числа с плавающей запятой, нас немало интересует округление, не так ли? Так вот, при обрезании числа со знаками после запятой, выходящими за заданный шаблон, DecimalFormat округляет число в большую сторону, если последнее обрезаемое число больше 5. А если последнее обрезаемое — 5? Ведь в таком случае это число ровно посередине между ближайшими целыми.В этом случае в расчет берется предыдущее до него число. Если предыдущее число чётное, округление производится:
DecimalFormat dF = new DecimalFormat("##.###"); String result = dF.format(56.4595); System.out.println((result));
DecimalFormat dF = new DecimalFormat("##.###"); String str = dF.format(56.4595); System.out.println((str));
Разницей между форматированием чисел с плавающей запятой с использованием String.format() и DecimalFormat.format() можно считать то, что в первом случае будут присутствовать конечные нули, даже если нет дробной части. Например:
String firstStr = String.format("%.4f", 9.00001273); System.out.println((firstStr));
DecimalFormat decimalFormat = new DecimalFormat("#.####"); String secondStr = decimalFormat.format(9.00001273); System.out.println((secondStr));
Как видите, при форматировании числа 9.00001273 с точностью до четырех десятичных разрядов метод format() у класса String выведет значение 9.0000 , при этом у DecimalFormat аналогичный метод format() выведет 9 .
BigDecimal и BigInteger
Раз мы затронули такую тему округления чисел в Java, поговорим и о том, как для таких операций использовать класс BigDecimal . Этот класс ориентирован на работу с действительно БОЛЬШИМИ числами: для него максимальные значения double и float слишком малы. У этого класса есть много различных настроек для округления числа с плавающей запятой, а также много методов для арифметических операций. У него есть похожий класс, но ориентированный на работу с БОЛЬШИМИ целыми числами — BigInteger . Подробнее о BigDecimal и BigInteger можно почитать в этой статье.
Форматирование Date и Time
Выше только упоминалось, что с помощью format() класса String можно еще и форматировать время и дату.Что же, давайте взглянем, как это делается. Во-первых, хотелось бы напомнить, что для дат используется спецификатор формата %t . Во-вторых, при форматировании шаблона, для каждого спецификатора формата для дат требуются дополнительные флаги форматирования. Вот возможные флаги форматирования для дат:
Флаги Описание %tB Полное название месяца, например, January, February и т.д. %tb Сокращенное название месяца, например, Jan, Feb и т.д. %tA Полное название дня недели, например, Sunday, Monday %ta Сокращенное название дня недели, например, Sun, Mon и т.д. %tY Год в формате 4 цифры, например, от 0000 до 9999 %ty Год в формате 2 цифры, например, от 00 до 99 %tm Месяц отформатирован с нуля в начале, например, от 01 до 12 %tc Дата и время в формате %ta %tb %td %tT %tZ %tY, например, Mon Feb 17 03:56:12 PST 2020 %tD Дата в формате %tm/%td/%ty %td День месяца в формате двух цифр, например, от 01 до 31 %te День месяца в формате без 0 в начале, например от 1 до 31 %tT Время в 24-часовом формате, например, %tH:%tM:%tS %tH Час дня в 24-часовом формате, от 00 до 23 %tI Час дня для 12-часового формата, например, от 01 до 12 %tM Минуты в часе форматируются с нуля в начале, например, от 00 до 59 %tS Секунды в минуте, состоящие из двух цифр, например, от 00 до 59 %tZ Аббревиатура часового пояса, например, PST, UTC и т.д. Это сокращенный список возможных флагов форматирования дат — их очень много, на любой вкус. Полный список их как и возможных спецификаторов можно посмотреть по этой ссылке. Давайте рассмотрим, как этим пользоваться. В этот раз используем не String.format() , а сразу System.out.printf() .
Пример 1
Date date = new Date(); System.out.printf(Locale.ENGLISH,"%tB %te, %tY",date,date,date);
Пример 2
Date date = new Date(); System.out.printf("%td %tB %tY года %n%tH:%tM:%tS",date,date,date,date,date,date,date);
Столько раз передавать аргументом один и тот же объект Date. Как-то выглядит не очень, не так ли? Давайте воспользуемся внутренней поднастройкой $ для указания аргумента, который мы хотим использовать:
System.out.printf("%1$td %1$tB %1$tY года %n%1$tH:%1$tM:%1$tS",date);
Вывод в консоли у нас и не изменится. Есть и другие не менее интересные способы форматирования даты. О них и немного подробнее о времени и дате в Java можно почитать вот в этом материале. На этом у меня на сегодня всё, спасибо за внимание!