11 июн. 2015 г.

Строки. Часть 8 – форматирование строк (теория).

В JDK 5 был введен механизм форматирования строк, подобный тому, что есть в операторе printf языка C. Этот механизм реализует метод format() класса String и многие другие методы, например System.out.printf().

Чтобы проще было понять о чем речь, перейдем сразу к практике и рассмотрим простой пример:

Str00044Число x можно вывести на консоль с помощью выражения System.out.println(x). В результате на экране отобразится число с максимальным количеством значащих цифр, допустимых для данного типа. Например, в результате выполнения приведенного ниже фрагмента кода на экран будет выведено число 3333.3333333333335.

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


Форматирование строк делает их восприятие более приятным и понятным. Посмотрим какой вывод генерирует данная программа:

Str00045Как видите такой вывод более приятен и понятен. Например, первый оператор printf форматирует число x таким образом (%.2f), что длина общего поля равна размеру всего числа, а десятичная часть занимает от этого только два числа (поля).

Второй оператор printf уже форматирует значение x в виде числа, размер поля которого составляет 8 цифр, а дробная часть равна двум цифрам (%8.2f). Можете посчитать и убедиться. Кроме того обратите внимание что между символом равно (=) и числом появился пробел, поскольку для всего поля числа отведено 8 символов и все не занятые поля заполняются пробелами и происходит выравнивание по правому краю. Следующий printf уже отвел под поле числа 16 символов (%16.2f), что тоже видно на выводе программы. И последним я продемонстрировал работу метода format() класса String. Они имеют абсолютно одинаковый синтаксис, так как их функционал основан на классе Formatter.

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

В методах printf() и format() можно задавать произвольное количество параметров. Пример вызова с несколькими параметрами приведен ниже.

System.out.printf("%s, в следующем году вам будет %d", name, age);

В данном случае значение переменной (параметра) name будет подставлено вместо спецификатора формата %s, а значение переменной (параметра) age – вместо спецификатора формата %d.

Каждый спецификатор формата, начинающийся с символа %, заменяется соответствующим параметром. Символ преобразования, которым завершается спецификатор формата, задает тип форматируемого значения: f — число с плавающей точкой; s — строка; d — десятичное число; и т.п. Символы преобразования описаны в таблице ниже:

Str00046
На заметку! Преобразование s можно использовать для форматирования любого объекта. Если этот объект реализует интерфейс Formattable, вызывается метод formatTo(). В противном случае для преобразования объекта в строку применяется метод toString().

Для даты и времени параметр х может быть представлен следующими символами преобразования:

DATE2
В составе спецификатора формата могут присутствовать флаги, управляющие форматом выходных данных. Назначение всех флагов описано в таблице ниже:

Str00047
Например, запятая, используемая в качестве флага, формирует разделитель групп, который зависит от локали (формата) установленной в вашей ОС. Так, в результате выполнения на Windwos 8.1 в русской локали, приведенного ниже оператора на экран будет выведена строка 3 333,33.

System.out.printf("%,.2f", 10000.0 / 3.0);

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

На заметку! Ряд правил форматирования зависит от специфических условий конкретной страны или региона.

В одном спецификаторе формата можно использовать несколько флагов, например последовательность символов "%, (.2f" указывает на то, что при выводе будут использованы разделители групп, а отрицательные числа будут помещены в скобки). Например строка:


System.out.printf("%, (.2f", -10000.0 / 3.0);

Сгенерирует вот такой вывод: (3 333,33)

Для создания форматированной строки без вывода ее можно использовать статический метод String.format(). Например:


String message = String.format("%s, в следующем году вам будет %d", name, age);

В строке, определяющей формат, может задаваться индекс форматируемого параметра. Индекс должен следовать непосредственно за символом % и завершаться знаком $. Пример использования индекса приведен ниже.


System.out.printf("%1$s %2$tB %2$te, %2$tY", "Дата:", new Date());

Данный код сформирует такой вывод: Дата: июня 11, 2015

Внимание! Индексы начинаются с единицы.

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


System.out.printf("%s %tB %<te, %<tY", "Дата:", new Date());

Чтобы все стало чуть более понятно, то, кратко, общий синтаксис можно описать так:

%[аргумент_индекс][флаги][ширина][.точность]символ_преобразования

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

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

Str00048
P.S. В следующем разделе будет больше практики. Я разделил теорию и практику чтобы было удобней смотреть на таблицы символов преобразования и примеры на разных вкладках, а не мотать одну страницу вверх и вниз.

4 комментария:

  1. Очень хорошо объяснили, спасибо.

    ОтветитьУдалить
  2. Спасибо за уточнение знака "+", долго не мог понять, что знак "-" показывает всегда, а знак "+" и так понятен и иногда его явно нужно указывать. Спасибо

    ОтветитьУдалить
  3. Хоть здесь описали общий синтаксис.

    ОтветитьУдалить