Перейти к основному содержимому

Руководство по java.util.Formatter

· 9 мин. чтения

1. Обзор

В этой статье мы обсудим форматирование строк в Java с использованием класса java.util.Formatter , который обеспечивает поддержку выравнивания и выравнивания макета.

2. Как использовать форматтер

Помните printf C? Форматирование строки в Java очень похоже.

Метод format() Formatter предоставляется через статический метод класса String . Этот метод принимает строку шаблона и список аргументов для заполнения шаблона:

String greetings = String.format(
"Hello Folks, welcome to %s !",
"ForEach");

Результирующая строка :

"Hello Folks, welcome to ForEach !"

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

В этом случае имеется единственный спецификатор формата %s , который заменяется соответствующим аргументом.

3. Спецификаторы формата

3.1. Общий синтаксис

Синтаксис спецификаторов формата для общего, символьного и числового типов:

%[argument_index$][flags][width][.precision]conversion

Спецификаторы аргумент_индекс, флаг, ширина и точность являются необязательными.

  • часть arguments_index представляет собой целое число i , указывающее, что здесь должен использоваться i -й аргумент из списка аргументов.
  • flags — это набор символов, используемых для изменения формата вывода.
  • ширина — положительное целое число, указывающее минимальное количество символов, которое должно быть записано в вывод.
  • точность — это целое число, обычно используемое для ограничения количества символов, конкретное поведение которых зависит от преобразования.
  • является обязательной частью. Это символ, указывающий, как должен быть отформатирован аргумент. Набор допустимых преобразований для данного аргумента зависит от типа данных аргумента.

В нашем примере выше, если мы хотим явно указать номер аргумента, мы можем записать его, используя индексы аргументов 1$ и 2$ .

Оба они являются первым и вторым аргументом соответственно:

String greetings = String.format(
"Hello %2$s, welcome to %1$s !",
"ForEach",
"Folks");

3.2. Для представления даты/времени

%[argument_index$][flags][width]conversion

Опять же , аргумент_индекс, флаги и ширина являются необязательными.

Давайте возьмем пример, чтобы понять это:

@Test
public void whenFormatSpecifierForCalendar_thenGotExpected() {
Calendar c = new GregorianCalendar(2017, 11, 10);
String s = String.format(
"The date is: %tm %1$te,%1$tY", c);

assertEquals("The date is: 12 10,2017", s);
}

Здесь для каждого спецификатора формата будет использоваться 1-й аргумент, следовательно, 1$ . Здесь, если мы пропустим аргумент_индекс для 2-го и 3-го спецификаторов формата, он попытается найти 3 аргумента, но нам нужно использовать один и тот же аргумент для всех 3 спецификаторов формата.

Итак, это нормально, если мы не указываем аргумент _index для первого, но нам нужно указать его для двух других.

Флаг здесь состоит из двух символов. Где первый символ всегда 't' или 'T' . Второй символ зависит от того, какая часть календаря должна отображаться.

В нашем примере первый спецификатор формата tm указывает месяц в формате двух цифр, te указывает день месяца, а tY указывает год в формате четырех цифр.

3.3. Спецификаторы формата без аргументов

%[flags][width]conversion

Необязательные флаги и ширина такие же, как определено в приведенных выше разделах.

Требуемое преобразование — это символ или строка , указывающая содержимое, которое будет вставлено в вывод. В настоящее время только «%» и новая строка «n» могут быть напечатаны с использованием этого:

@Test
public void whenNoArguments_thenExpected() {
String s = String.format("John scored 90%% in Fall semester");

assertEquals("John scored 90% in Fall semester", s);
}

Внутри format() , если мы хотим напечатать '%', нам нужно экранировать его, используя '%%' .

4. Конверсии

Давайте теперь углубимся во все детали синтаксиса спецификатора формата, начиная с преобразования . Обратите внимание, что вы можете найти все подробности в javadocs Formatter .

Как мы заметили в примерах выше, конверсионная часть обязательна во всех спецификаторах формата, и ее можно разделить на несколько категорий.

Давайте рассмотрим каждый из них на примерах.

4.1. Общий

Используется для любого типа аргумента. Общие преобразования:

  1. 'b' или 'B' – для логических значений
  2. 'h' или 'H' - для HashCode
  3. 's' или 'S' — для String , если null , печатается «null», иначе arg.toString()

Теперь попробуем отобразить логические и строковые значения, используя соответствующие преобразования:

@Test
public void givenString_whenGeneralConversion_thenConvertedString() {
String s = String.format("The correct answer is %s", false);
assertEquals("The correct answer is false", s);

s = String.format("The correct answer is %b", null);
assertEquals("The correct answer is false", s);

s = String.format("The correct answer is %B", true);
assertEquals("The correct answer is TRUE", s);
}

4.2. Персонаж

Используется для основных типов, представляющих символы Юникода: char, Character, byte, Byte, short и Short . Это преобразование также можно использовать для типов int и Integer , когда для них Character.isValidCodePoint(int) возвращает значение true .

Его можно записать как «с» или «С» в зависимости от того, какой регистр нам нужен.

Попробуем напечатать некоторые символы:

@Test
public void givenString_whenCharConversion_thenConvertedString() {
String s = String.format("The correct answer is %c", 'a');
assertEquals("The correct answer is a", s);

s = String.format("The correct answer is %c", null);
assertEquals("The correct answer is null", s);

s = String.format("The correct answer is %C", 'b');
assertEquals("The correct answer is B", s);

s = String.format("The valid unicode character: %c", 0x0400);
assertTrue(Character.isValidCodePoint(0x0400));
assertEquals("The valid unicode character: Ѐ", s);
}

Возьмем еще один пример недопустимой кодовой точки:

@Test(expected = IllegalFormatCodePointException.class)
public void whenIllegalCodePointForConversion_thenError() {
String s = String.format("The valid unicode character: %c", 0x11FFFF);

assertFalse(Character.isValidCodePoint(0x11FFFF));
assertEquals("The valid unicode character: Ā", s);
}

4.3. Числовой – Интегральный

Они используются для целочисленных типов Java: byte, Byte, short, Short, int и Integer, long, Long и BigInteger . В этой категории есть три преобразования:

  1. 'd' - для десятичного числа
  2. 'o' - для восьмеричного числа
  3. «X» или «x» - для шестнадцатеричного числа

Попробуем распечатать каждое из них:

@Test
public void whenNumericIntegralConversion_thenConvertedString() {
String s = String.format("The number 25 in decimal = %d", 25);
assertEquals("The number 25 in decimal = 25", s);

s = String.format("The number 25 in octal = %o", 25);
assertEquals("The number 25 in octal = 31", s);

s = String.format("The number 25 in hexadecimal = %x", 25);
assertEquals("The number 25 in hexadecimal = 19", s);
}

4.4. Числовой — с плавающей запятой

Используется для типов Java с плавающей запятой: float, Float, double, Double и BigDecimal .

  1. «e» или «E» - отформатировано как десятичное число в компьютеризированной экспоненциальной записи .
  2. 'f' - отформатировано как десятичное число
  3. «g» или «G» - в зависимости от значения точности после округления это преобразование форматирует в компьютеризированную экспоненциальную запись или десятичный формат.

Попробуем напечатать числа с плавающей запятой:

@Test
public void whenNumericFloatingConversion_thenConvertedString() {
String s = String.format(
"The computerized scientific format of 10000.00 "
+ "= %e", 10000.00);

assertEquals(
"The computerized scientific format of 10000.00 = 1.000000e+04", s);

String s2 = String.format("The decimal format of 10.019 = %f", 10.019);
assertEquals("The decimal format of 10.019 = 10.019000", s2);
}

4.5. Другие преобразования

  • Date/Time — для типов Java, которые могут кодировать дату или время: long, Long, Calendar, Date и TemporalAccessor. Для этого нам нужно использовать префикс «t» или «T» , как мы видели ранее.
  • Процент — печатает литерал '%' ('\ u0025')
  • Разделитель строк — печатает разделитель строк для конкретной платформы.

Давайте посмотрим на простой пример:

@Test
public void whenLineSeparatorConversion_thenConvertedString() {
String s = String.format("First Line %nSecond Line");

assertEquals("First Line \n" + "Second Line", s);
}

5. Флаги

Флаги, как правило, используются для форматирования вывода. Принимая во внимание, что в случае даты и времени они используются для указания того, какая часть даты должна отображаться, как мы видели в примере из раздела 4.

Доступен ряд флагов, список которых можно найти в документации.

Давайте посмотрим на пример флага, чтобы понять его использование. '-' используется для форматирования вывода с выравниванием по левому краю:

@Test
public void whenSpecifyFlag_thenGotFormattedString() {
String s = String.format("Without left justified flag: %5d", 25);
assertEquals("Without left justified flag: 25", s);

s = String.format("With left justified flag: %-5d", 25);
assertEquals("With left justified flag: 25 ", s);
}

6. Точность

Для общих преобразований точность — это просто максимальное количество символов, которое должно быть записано в вывод . Принимая во внимание, что для преобразований с плавающей запятой точность - это количество цифр после точки счисления.

Первый оператор является примером точности с числами с плавающей запятой, а второй — с общими преобразованиями:

@Test
public void whenSpecifyPrecision_thenGotExpected() {
String s = String.format(
"Output of 25.09878 with Precision 2: %.2f", 25.09878);

assertEquals("Output of 25.09878 with Precision 2: 25.10", s);

String s2 = String.format(
"Output of general conversion type with Precision 2: %.2b", true);

assertEquals("Output of general conversion type with Precision 2: tr", s2);
}

7. Индекс аргумента

Как упоминалось ранее, arguments_index — это целое число, указывающее позицию аргумента в списке аргументов . 1$ указывает на первый аргумент, 2$ на второй аргумент и так далее. ``

Кроме того, есть еще один способ ссылаться на аргументы по положению, используя флаг '<' ('\u003c') , что означает, что аргумент из предыдущего спецификатора формата будет повторно использован. Например, эти два оператора будут давать одинаковый результат:

@Test
public void whenSpecifyArgumentIndex_thenGotExpected() {
Calendar c = Calendar.getInstance();
String s = String.format("The date is: %tm %1$te,%1$tY", c);
assertEquals("The date is: 12 10,2017", s);

s = String.format("The date is: %tm %<te,%<tY", c);
assertEquals("The date is: 12 10,2017", s);
}

8. Другие способы использования Formatter

До сих пор мы видели использование метода format() класса Formatter . Мы также можем создать экземпляр Formatter и использовать его для вызова метода format() .

Мы можем создать экземпляр, передав Appendable , OutputStream , File или имя файла . Исходя из этого, отформатированная строка сохраняется в Appendable , OutputStream , File соответственно.

Давайте посмотрим на пример его использования с Appendable. Точно так же мы можем использовать его с другими.

8.1. Использование Formatter с Appendable

Давайте создадим экземпляр StringBuilder sb и с его помощью создадим Formatter . Затем мы вызовем format() для форматирования строки :

@Test
public void whenCreateFormatter_thenFormatterWithAppendable() {
StringBuilder sb = new StringBuilder();
Formatter formatter = new Formatter(sb);
formatter.format("I am writting to a %s Instance.", sb.getClass());

assertEquals(
"I am writting to a class java.lang.StringBuilder Instance.",
sb.toString());
}

9. Заключение

В этой статье мы рассмотрели средства форматирования, предоставляемые классом java.util.Formatter . Мы видели различный синтаксис, который можно использовать для форматирования String , и типы преобразования, которые можно использовать для разных типов данных.

Как обычно, код примеров, которые мы видели, можно найти на Github .