Укрощаем "Тигра": форматированный вывод

Укрощаем "Тигра": форматированный вывод

Наконец Java-разработчики дождались. Возможность форматированного вывода в стиле функции printf языка программирования C появилась и в Java. Это свершилось с выходом новой версии J2SE 1.5 (кодовое имя "Тигр"), которую совсем недавно выпустила в свет компания Sun Microsystems. В этой статье мы посмотрим на реализацию этой возможности и научимся использовать ее на практике.

В оригинале возможность форматированного ввода планировалось ввести еще в J2SE версии 1.4. Но в силу сложившихся обстоятельств, скорее всего из-за нехватки времени, в версии 1.4 форматированный вывод реализован не был. Но с "Тигром" ситуация поменялась: наряду с множеством других нововведений у нас теперь нет необходимости вручную склеивать строки и переменные в одну общую строку.
Для тех, кто никогда не программировал на C и не сталкивался с понятием форматированного вывода, следует раскрыть это понятие. Форматированная строка вывода — это строка, которая содержит список параметров и определяет порядок и правила формирования результативной строки с подстановкой в нее переменных-аргументов. Для форматирования используются специальные символы и их комбинации. Например, вместо того, чтобы склеивать (конкатенировать) строки (hours + ":" + minutes), вы можете задать специальную строку, которая будет описывать правила формирования результативной строки и заменять все заполнители, использованные в этой строке, аргументами, заданными в конце вызова специального метода, например:

String str = String.format ("%1$s:%2$s", hours, minutes)

Класс Formatter
Для начала рассмотрим новый класс java.util.Formatter. Наверняка вы не будете слишком часто прибегать к его использованию напрямую, но он содержит основную логику всего форматирования, которым вы будете пользоваться. В документации Javadoc к этому классу можно найти большую таблицу с описанием поддерживаемых опций форматирования. Эти опции варьируются от простого задания точности и ширины дробного числа (%7,4f) до форматирования времени (%tT) или, например, третьего аргумента (%3$s).

Для форматирования вывода с помощью класса Formatter необходимо пройти следующие два этапа: создать объект Appendable для хранения вывода и с помощью метода format() передать форматируемую информацию этому объекту. Вот список классов, реализующих интерфейс Appendable:

• BufferedWriter;
• CharArrayWriter;
• CharBuffer;
• FileWriter;
• FilterWriter;
• LogStream;
• OutputStreamWriter;
• PipedWriter;
• PrintStream;
• PrintWriter;
• StringBuffer;
• StringBuilder;
• StringWriter;
• Writer.

Объект, который реализует этот интерфейс, может использоваться как целевой, т.е. когда при использовании класса Formatter этот объект передается конструктору класса Formatter. Большинство этих классов выглядят одинаково, кроме класса StringBuilder. Класс StringBuilder больше схож с классом String Buffer, но с одним большим "но": он не обеспечивает должного уровня безопасности при использовании в многопоточной среде. Поэтому если вы уверены, что будете строить строку в однопоточной среде, используйте StringBuilder. Если же вы работаете с приложением, состоящим из нескольких потоков, то лучше обойтись классом StringBuffer. Ниже приведен листинг, демонстрирующий типичное начало использования класса Formatter:

StringBuilder sb = new StringBuilder();
Formatter formatter = new Formatter(sb, Locale.US);

После создания класса Formatter вызываем его метод format(), которому передаем строки с шаблоном форматирования и аргументы. Если вам вдруг понадобится использовать не ту локализацию, которую вы указали при создании класса, вы можете передать объект класса Locale методу format(). Следующий листинг демонстрирует два варианта использования метода format():

public Formatter format(String format, Object... args)
public Formatter format(Locale l, String format, Object... args)

Предположим, вам надо получить значение числа Пи с точностью до 10 цифр. Ниже представлен код, который передает это значение в класс StringBuilder и выводит результат на экран. Печатая объект Formatter, мы выводим на экран содержимое объекта Appendable.

import java.util.Locale;
import java.util.Formatter;

public class PiSample {
public static void main(String args[]) {
StringBuilder sb = new StringBuilder();
Formatter formatter = new Formatter (sb, Locale.US);
formatter.format("PI = %12.10f", Math.PI);
System.out.println(formatter);
}
}

Не забудьте компилировать с опцией "-source 1.5", иначе компилятор просто не распознает список переменных-аргументов.

Поддержка PrintStream
Класс PrintStream содержит объявления общих объектов System.out и System.err для вывода в стандартный поток вывода и стандартный поток ошибок соответственно. "Тигр" вводит 2 новых конструктора (для прямого вывода в файл) и 6 методов для поддержки форматирования (3 набора пар). Первая пара — это разные версии метода append(). Эта пара методов реализует новый интерфейс java.lang.Appendable. Скорее всего, вы не будете пользоваться этими методами напрямую. Чаще вы будете вызывать методы format() и printf(), где printf() — это всего лишь удобная "обертка" методов format():

public PrintStream format(String format, Object... args)
public PrintStream format(Locale l, String format, Object... args)
В следующем листинге представлен пример использования метода format() для вывода текущей даты:
import java.util.Calendar;

public class Now {
public static void main(String args[]) {
System.out.format(
"Сегодня %1$tB %1$te, %1$tY.",
Calendar.getInstance()
);
}
}

Поддержка String
Класс String имеет 2 новых статических метода format(), которые работают так же, как и эквивалентные им методы printf(). Передаем шаблон строки форматирования и аргументы (возможно еще и объект Locale для локализации), которые используются для формирования результативной строки с подставленными в шаблон аргументами. Однако в случае с версиями методов класса String результат возвращается обратно в объект String, вместо того, чтобы передаваться куда-то там дальше через поток (stream). Хотя очевидно, что эти методы не впечатляют своей навороченностью, они позволяют вам избежать прямого использования объекта Formatter и создания посредника в облике объекта StringBuilder.

Форматирование произвольных объектов
Все, что мы рассмотрели до настоящего времени, описывало лишь использование новых возможностей форматирования существующих объектов и базовых типов. Если же вы хотите использовать свои собственные объекты с объектом Formatter, то в этом вам может помочь специальный интерфейс Formattable. Реализовав метод formatTo() в вашем классе, вы можете использовать этот класс в качестве аргумента при форматировании строк:

void formatTo(Formatter formatter,
int flags,
Integer width,
Integer precision)

Теперь продемонстрируем использование интерфейса Formattable посредством предоставления простому классу свойства name. Это имя будет отображаться в выводе. Плюс этим классом будет осуществляться контроль над шириной вывода и выравниванием:

import java.util.Locale;
import java.util.Formatter;
import java.util.Formattable;

public class MyObject implements Formattable {
String name;
public MyObject(String name) {
this.name = name;
}

public void formatTo(
Formatter fmt,
int f,
Integer width,
Integer precision) {
StringBuilder sb = new StringBuilder();
if (precision == null) {
sb.append(name);
} else if (name.length() < precision) {
sb.append(name);
} else {
sb.append(name.substring(0, precision — 1)).append('*');
}

// Применяем выравнивание и задаем ширину
if ((width != null) && (sb.length() < width)) {
for (int i = 0, n=sb.length(); i < width — n; i++) {
if ((f & Formattable.LEFT_JUSTIFY) == Formattable.LEFT_JUSTIFY) {
sb.append(' ');
} else {
sb.insert(0, ' ');
}
}
}
fmt.format(sb.toString());
}

public static void main(String args[]) {
MyObject my1 = new MyObject("Алексей");
MyObject my2 = new MyObject("Просто строка");
// Используем метод toString()
System.out.println("1 : " + my1);
// Используем Formatter
System.out.format("1 : '%s'\n", my1);
// Используем Formatter
System.out.format("2: '%s'\n", my2);
// Используем Formatter с заданием ширины
System.out.format("2: '%10.5s'\n", my2);
// Используем Formatter с заданием ширины и выравниванием слева
System.out.format("2: '%-10.5s'\n", my2);
}

Запустив эту программу, на выходе мы получим следующее:

1 : MyObject@10b62c9
1 : 'Алексей'
2: 'Просто строка'
2: ' Прос*'
2: 'Прос* '

Первые две строки демонстрируют разницу использования метода toString() и класса Formatter. Оставшиеся три показывают использование опций задания ширины и выравнивания.

Резюме
Чтобы разобраться со всеми опциями форматирования, представленными в классе Formatter, вам понадобится совсем немного времени. Особенно если до этого вы имели опыт работы с языком программирования C. Хотя некоторые незначительные различия все же есть, но в большинстве своем все сделано в старых традициях. Ключевое различие состоит в том, что в случае с платформой Java при неправильно построенной строке-шаблоне форматирования возникнет исключение. Вот и все.

Что еще почитать?
1. Java 2 Standard Edition версии 1.5 ("Тигр") можно скачать отсюда: http://java.sun.com/j2se/1.5.0 /
2. Javadocs рассмотренных в этой статье классов и интерфейсов можно посмотреть здесь:
• Formatter — http://java.sun.com/j2se/1.5.0/docs/api/java/util/Formatter.html
• Formattable — http://java.sun.com/j2se/1.5.0/docs/api/java/util/Formattable.html
• Appendable — http://java.sun.com/j2se/1.5.0/docs/api/java/lang/Appendable.html
• String — http://java.sun.com/j2se/1.5.0/docs/api/java/lang/String.html
• StringBuilder — http://java.sun.com/j2se/1.5.0/docs/api/java/lang/StringBuilder.html
• PrintStream — http://java.sun.com/j2se/1.5.0/docs/api/java/io/PrintStream.html

По материалам John Zukowski
Алексей Литвинюк, litvinuke@tut.by, http://java.linux.by




Компьютерная газета. Статья была опубликована в номере 32 за 2004 год в рубрике программирование :: разное

©1997-2024 Компьютерная газета