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. Общий
Используется для любого типа аргумента. Общие преобразования:
'b'
или'B'
– длялогических
значений'h'
или'H'
- дляHashCode
'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
. В этой категории есть три преобразования:
'd'
- для десятичного числа'o'
- для восьмеричного числа«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 .
«e»
или«E»
- отформатировано как десятичное число в компьютеризированной экспоненциальной записи .'f'
- отформатировано как десятичное число«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 .