Как отформатировать строку в Java с примерами

Введение В Java существует несколько способов форматирования строк. Некоторые из них являются олдскульными и заимствованы непосредственно из старой классики (например, printf из C), в то время как другие больше в духе объектно-ориентированного программирования, например, класс MessageFormat. В этой статье мы рассмотрим несколько из этих подходов. Мы покажем некоторые особенности того, как можно использовать каждый из методов и в каких обстоятельствах. Используя эти знания, вы узнаете, как подойти к форматированию строк и какие

Вступление

В Java существует несколько способов форматирования строк. Некоторые из них являются олдскульными и заимствованы непосредственно из старой классики (например, printf из C), в то время как другие больше в духе объектно-ориентированного программирования, например, класс MessageFormat

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

System.out.printf ()

Начнем со старой классики printf() . Как упоминалось ранее, printf() происходит от языка программирования C и означает форматирование для печати. Под капотом printf() использует java.util.Formatter , о котором мы поговорим позже.

Принцип работы printf() можно объяснить ее аргументами. Наиболее распространенный способ использования printf() следующий:

 System.out.printf(String format, String... arguments); 

Мы видим, что метод ожидает format и arguments vararg. format определяет способ форматирования строки - шаблон для окончательного результата.

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

arguments vararg обычно ожидают аргументы (т. Е. Значения) для строки шаблона. Например, если в шаблоне есть заполнители для двух чисел, метод printf() также будет ожидать два числа в качестве arguments :

 System.out.printf("%d %d", 42, 23); 

Мы поместили два %d в строку шаблона. Эти два символа представляют собой заполнители для определенного типа значения. Например, %d - это десятичное числовое значение. Поскольку у нас их два, мы должны передать два аргумента, которые соответствуют числовым значениям, например 42 и 23 .

Запуск этого кода даст:

 42 23 

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

С помощью printf() вы можете печатать такие значения, как числа, строки, даты и т. Д. Чтобы метод знал, что именно вы пытаетесь напечатать, вам необходимо предоставить спецификатор формата для каждого из значений. Давайте посмотрим на пример:

 System.out.printf("Hello, %s!", "reader"); 

При выполнении этот код напечатает Hello, reader в консоль. Символ %s представляет описатель формата для строк, аналогично тому, как %d представляет описатель формата для десятичных чисел.

Мы можем использовать множество спецификаторов формата. Вот несколько распространенных:

  • % c - символ
  • % d - десятичное число (основание 10)
  • % e - экспоненциальное число с плавающей запятой
  • % f - число с плавающей запятой
  • % i - целое число (основание 10)
  • % o - восьмеричное число (основание 8)
  • % s - Строка
  • % u - беззнаковое десятичное (целое) число
  • % x - шестнадцатеричное число (основание 16)
  • % t - Дата / время
  • % n - Новая строка

Если мы хотим напечатать, например, символ и восьмеричное число, мы должны использовать %c и %o соответственно. Вы можете заметить кое-что необычное: спецификатор новой строки. Если вы не привыкли к printf() из C, может показаться немного странным указывать такие вещи.

Ну, printf() по умолчанию не записывает новую строку. Фактически, по умолчанию он почти ничего не делает. По сути, если вы хотите, чтобы что-то произошло, вы должны сделать это сами.

То есть - если у нас есть несколько printf() без спецификатора новой строки:

 System.out.printf("Hello, %s!", "Michael Scott"); 
 System.out.printf("Hello, %s!", "Jim"); 
 System.out.printf("Hello, %s!", "Dwight"); 

Результат будет:

 Hello, Michael Scott!Hello, Jim!Hello, Dwight! 

Хотя, если мы включим символ новой строки:

 System.out.printf("Hello, %s!%n", "Michael Scott"); 
 System.out.printf("Hello, %s!%n", "Jim"); 
 System.out.printf("Hello, %s!%n", "Dwight"); 

Тогда результат будет:

 Hello, Michael Scott! 
 Hello, Jim! 
 Hello, Dwight! 

Примечание. %n - это специальный формат, который может быть либо \r\n либо просто \n . \n - это фактический символ новой строки, а \r - это символ возврата каретки. Обычно рекомендуется использовать \n поскольку он работает должным образом во всех системах, в отличие от %n который можно понимать как любой из двух. Подробнее об этом позже.

Персонажи побега

В дополнение к описанным выше описателям формата существует еще один тип символов форматирования: escape-символы.

Давайте представим , что мы хотим напечатать " символ с помощью printf() Мы можем попробовать что - то вроде.:

 System.out.printf("""); 

Если вы попытаетесь запустить это, ваш компилятор наверняка выдаст исключение. Если вы присмотритесь, даже код, который выделяет код на этой странице, будет выделен ); как String, а не закрытая скобка метода.

Случилось так, что мы попытались напечатать символ, имеющий особое, зарезервированное значение. Кавычки используются для обозначения начала и конца строки.

Мы начали и закончили строку "" , после чего мы открыли еще один " , но не закрыл его. Это делает печать зарезервированных символов , как это невозможно, используя этот подход.

Способ обойти это - убежать . Чтобы напрямую печатать специальные символы (такие как " ), нам нужно сначала избежать его эффектов, а в Java это означает префикс обратной косой черты ( \ ). Чтобы официально печатать кавычки в Java, мы должны сделать следующее:

 System.out.printf("\""); 

Сочетание \ и " определенно говорит компилятору , что мы хотели бы, чтобы вставить " символ в том месте , и что он должен относиться к " в качестве значения конкретного, а не зарезервированный символ.

Применение escape-символа \ может вызывать различные эффекты в зависимости от последующего. Передача обычного символа (незарезервированного) ничего не даст, а \ будет рассматриваться как значение.

Однако некоторые комбинации (также называемые командами) имеют для компилятора другое значение:

  • \ b - Вставить пробел
  • \ f - Первый символ следующей строки начинается справа от последнего символа текущей строки
  • \ n - вставить новую строку
  • \ r - вставить возврат каретки
  • \ t - Вставить вкладку
  • \\ - Вставить обратную косую черту
  • %% - вставить знак процента

Таким образом, вы должны использовать \n для вывода разделителя строк на консоль, эффективно начиная любое новое содержимое с начала следующей строки. Точно так же, чтобы добавить вкладки, вы должны использовать спецификатор \t

Вы могли заметить %% как последнюю комбинацию.

Почему это? Почему просто не используется \%

Символ % уже является escape-символом специально для метода printf() За которыми следуют такие символы, как d , i , f и т. Д., Средство форматирования во время выполнения знает, как обрабатывать эти значения.

Однако \ предназначен для компилятора. Он сообщает, куда и что вставлять. Команда \% просто не определена, и мы используем % чтобы избежать эффекта последующего % - если это имеет смысл.

Для компилятора % не является специальным символом, а является \ Кроме того, существует соглашение, согласно которому специальные символы избегают самих себя. \ экранирует \ и % экранирует % .

Основное использование

Давайте отформатируем строку с несколькими аргументами разных типов:

 System.out.printf("The quick brown %s jumps %d times over the lazy %s.\n", "fox", 2, "dog"); 

Результатом будет:

 The quick brown fox jumps 2 times over the lazy dog. 

Поплавок и двойная точность

С помощью printf() мы можем определить настраиваемую точность для чисел с плавающей запятой:

 double a = 35.55845; 
 double b = 40.1245414; 
 
 System.out.printf("a = %.2f b = %.4f", a, b); 

Поскольку %f используется для чисел с плавающей запятой, мы можем использовать его для вывода double s. Однако, добавив .n , где n - количество десятичных знаков, мы можем определить настраиваемую точность.

Выполнение этого кода дает:

 a = 35.56 
 b = 40.1245 

Форматирование заполнения

Мы также можем добавить отступы, включая переданную строку:

 System.out.printf("%10s\n", "stack"); 

Здесь после % мы передали число и спецификатор формата. В частности, нам нужна строка из 10 символов, за которой следует новая строка. Поскольку stack содержит только 5 символов, еще 5 добавляются в качестве отступа, чтобы «заполнить» строку до целевого символа:

 stack 

Вместо этого вы также можете добавить правый отступ:

 System.out.printf("%-10s\n", "stack"); 

Locale

Мы также можем передать Locale в качестве первого аргумента, форматируя строку в соответствии с ним:

 System.out.printf(Locale.US, "%,d\n", 5000); 
 System.out.printf(Locale.ITALY, "%,d\n", 5000); 

Это даст два целых числа в разных форматах:

 5,000 
 5.000 

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

Если индекс аргумента не указан, аргументы будут просто следовать порядку присутствия в вызове метода:

 System.out.printf("First argument is %d, second argument is %d", 2, 1); 

Это приведет к:

 First argument is 2, argument number is 1 

Однако после % и перед спецификатором формата мы можем добавить другую команду. $n укажет индекс аргумента:

 System.out.printf("First argument is %2$d, second argument is %1$d", 2, 1); 

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

Выполнение этого кода приводит к:

 First argument is 1, second argument is 2 

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

 System.out.printf("First argument is %2$d, second argument is %2$d", 2, 1); 

Это приведет к:

 First argument is 1, second argument is 1 

System.out.format ()

Прежде чем говорить о System.out.format() , давайте кратко остановимся на System.out .

Все системы UNIX имеют три основных канала - стандартный канал ввода ( stdin ), стандартный канал вывода ( stdout ) и стандартный канал ошибок ( stderr ). Поле out соответствует PrintStream stdout и имеет тип PrintStream.

Этот класс имеет много различных методов для печати форматированных текстовых представлений в поток, некоторые из которых - это format() и printf() .

Согласно документации, они оба ведут себя одинаково . Это означает, что между ними нет разницы, и их можно использовать для получения тех же результатов. Все, что мы до сих пор говорили о printf() также работает для format() .

И printf() и System.out.format() печатают в stdout , который обычно нацелен на консоль / терминал.

String.format ()

Другой способ форматирования строк - String.format() который внутренне также использует java.util.Formatter , который мы рассмотрим в следующем разделе.

Основное преимущество String.format() перед printf() - это его возвращаемый тип - он возвращает String . Вместо того, чтобы просто печатать содержимое в стандартном канале вывода и не иметь возвращаемого типа ( void ), как printf() , String.format() используется для форматирования строки, которую можно использовать или повторно использовать в будущем:

 String formattedString = String.format("Local time: %tT", Calendar.getInstance()); 

Теперь вы можете делать все , что вы хотели бы в formattedString . Вы можете распечатать его, вы можете сохранить его в файл, вы можете изменить его или сохранить в базе данных. Его печать приведет к:

 Local time: 16:01:42 

Метод String.format() использует тот же базовый принцип, что и метод printf() . Оба внутренне используют Formatter для фактического форматирования строк. Таким образом, все, что сказано для printf() также применимо к String.format() .

Использование printf() , String.format() или Formatter по сути одно и то же. Единственное, что отличается, - это тип возвращаемого значения - printf() печатает в стандартный поток вывода (обычно в вашу консоль), а String.format() возвращает отформатированную String .

При этом String.format() более универсален, поскольку вы можете использовать результат более чем одним способом.

Класс Formatter

Поскольку все вышеперечисленные методы по своей сути вызывают Formatter , знание только одного означает, что вы знаете их все.

Использование Formatter очень похоже на другие методы, показанные ранее. Самая большая разница в том, что для его использования нужно создать экземпляр объекта Formatter

 Formatter f = new Formatter(); 
 f.format("There are %d planets in the Solar System. Sorry, Pluto", 8); 
 System.out.println(f); 

Напрашивается вопрос:

Почему бы мне всегда просто не использовать предыдущие методы, поскольку они более краткие?

Есть еще одно важное отличие, делающее Formatter достаточно гибким:

 StringBuilder sb = new StringBuilder(); 
 Formatter formatter = new Formatter(sb); 
 
 formatter.format("%d, %d, %d...\n", 1, 2, 3); 

Вместо того, чтобы работать только с String s, Formatter также может работать с StringBuilder что позволяет (повторно) эффективно использовать оба класса.

Фактически, Formatter может работать с любым классом, реализующим интерфейс Appendable Одним из таких примеров является вышеупомянутый StringBuilder , но другие примеры включают такие классы, как BufferedWriter , FileWriter , PrintStream , PrintWriter , StringBuffer и т. Д. Полный список можно найти в документации .

Наконец, все спецификаторы формата, escape-символы и т. Д. Также действительны для Formatter поскольку это основная логика для форматирования строк во всех трех случаях: String.format() , printf() и Formatter .

MessageFormat

Наконец, давайте покажем еще один последний метод форматирования, который не использует Formatter под капотом.

MessageFormat был создан для создания и предоставления объединенных сообщений независящим от языка способом. Это означает, что форматирование будет одинаковым, независимо от того, используете ли вы Java, Python или какой-либо другой язык, поддерживающий MessageFormat .

MessageFormat расширяет абстрактный Format точно так же, как DateFormat и NumberFormat . Класс Format предназначен для форматирования объектов, зависящих от языкового стандарта, в строки.

Давайте посмотрим , хороший пример, любезно MessageFormat «s документации .

 int planet = 7; 
 String event = "a disturbance in the Force"; 
 
 String result = MessageFormat.format( 
 "At {1, time} on {1, date}, there was {2} on planet {0, number, integer}.", 
 planet, new Date(), event 
 ); 

[Кредит кода: Oracle Docs]{.small}

Результат:

 At 11:52 PM on May 4, 2174, there was a disturbance in the Force on planet 7. 

Вместо описателей процентов, которые мы видели до сих пор, здесь мы используем фигурные скобки для каждого аргумента. Возьмем первый аргумент, {1, time} . Число 1 представляет собой индекс аргумента, который следует использовать вместо него. В нашем случае аргументами являются planet , new Date() и event .

Вторая часть, time , относится к типу значения. Типы формата верхнего уровня - это number , date , time и choice . Для каждого из значений можно сделать более конкретный выбор, например, с помощью {0, number, integer} который говорит, что значение следует рассматривать не только как число, но и как целое число.

Полный набор типов и подтипов форматов можно найти в документации .

Заключение

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

У каждой из представленных нами техник есть своя причина существования. printf() , например, напоминает одноименный метод C старой школы из.

Другие подходы, такие как Formatter или MessageFormat предлагают более современный подход, который использует некоторые преимущества объектно-ориентированного программирования.

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

Licensed under CC BY-NC-SA 4.0
comments powered by Disqus