Полезное для программистов:

Фриланс
Новости
Статьи
   
Рубрики:


Форматирование чисел, дат и текста

Поиск:
Форматирование чисел, дат и текста.


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

Базовым классом отвечающим за форматирование является класс java.text.Format. Он определяет два базовых метода для всех "форматеров": public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) и public Object parseObject(String source, ParsePosition pos), именно их и надо перереопределить для реализации своего форматера. Для форматирования есть несколько вспомогательных классов DecimalFormatSymbols и DateFormatSymbols, эти классы содержат вспомогательны данные используемые при форматировании (название дней недели и месяцев, десятичный разделитель, символ валюты и т.п.).

Первым рассмотрим класс DecimalFormatDecimalFormat предназначен для форматирования десятичных чисел (с другими системами счисления он к сожалению не работает). Создаем экземпляр DecimalFormat, при создании можно указать шаблон по которому форматировать числа и DecimalFormatSymbols. В шаблоне используются следующие символы:
  •  0 - цифра, не значащие нули показываются
  •  # - цифра, не значащие нули не показываются
  •  . - десятичная точка, в шаблоне она всегда пишется как точка, независимо от того какой символ (точка или запятая) используется для форматирования
  •  - - знак минус
  •  , - разделитель групп разрядов, те же правила что и с точкой
  •  E - разделитель мантисы и экспоненты в научной нотации
это основные символы для задания шаблона. Любые символы не являющиеся частью шаблонов выводятся как есть, если есть необходимость в шаблоне вывести символы являющиеся частью шаблона, то такие символы надо взять в апострофы.

Задача: вывести число с точностью семь цифр до запятой и две после, и разделением на разряды по 2 цифры в группе
Код
DecimalFormat df = new DecimalFormat("0,00,00,00.00");
System.out.println(df.format(12348.14159265359));

получим 0 01 23 48,14.

В шаблоне указатель групп разрядов достаточно указать один раз, в нашем примере можно было составить и такой шаблон 00000,00.00, он даст тот же самый эффект. При форматировании число может быть округленно до указанного количества знаков после запятой, но значищие цифры слева от запятой отброшенны не будут не зависимо от шаблона. Если выполнить:
Код
DecimalFormat df = new DecimalFormat("00,000.00");
System.out.println(df.format(123456789.123456789));

то получим 123 456 789,12.

Иногда бывает не удобно задавать количество цифр с помщью шаблона (например шаблон формируется не нами). Для этих целей можно использовать setMaximumFractionDigits()setMinimumFractionDigits() - для дробной части и setMinimumIntegerDigits() и setMaximumIntegerDigits() - для целой части, количество цифр в группе можно установить с помощью setGroupingSize().

Задача: задать количество выводимых цифр не меняя шаблона:
Код
DecimalFormat df = new DecimalFormat();
df.setMaximumFractionDigits(2);
df.setMinimumFractionDigits(6);
df.setMinimumIntegerDigits(3);
df.setMaximumIntegerDigits(5);
df.setGroupingSize(3);
System.out.println(df.format(123456789.123456789));

получим 56 789,123457.

Теперь рассмотрим более специфические задачи.

Часто бывает необходимо вывести число в виде процентов для этих целей можно использовать символ %, если в шаблоне встречается символ %, то перед форматированием число умножается на 100.

Задача: вывести число в виде процентов:
Код
DecimalFormat df = new DecimalFormat("0.00%");
System.out.println(df.format(1.23456789));

получим 123,46%, аналогично с процентам можно использовать промиле, вместо символа процентов надо использовать символ промиле \u2030.

Другая распространенная задача, это вывод денежных единиц. Для этих целей служит символ \u00A4, использованный в единственном числе он выводит национальное обозначение валюты (например руб. или $), а если его удвоить \u00A4\u00A4 то быдет выведено международное обозначение валюты (RUB и USD соответсвенно). Если в шаблоне присутствует символ \u00A4, то символ десятичной точки заменяется на денежный разделитель (в большинстве стран он совпадает с десятичной точкой). Иногда бывает необходимо разделить шаблоны для положительных и отрицательных величин, для этого используется символ ;, все что идет до него считается шаблоном для положительных чисел, а все что после - для отрицательных.

Задача: вывести число с точностью до двух знаков после запятой и символом валюты, для положительных чисел перед числом надо поставить (+), а для отрицательных (-):
Код
DecimalFormat df = new DecimalFormat("'(+)'0.00\u00A4;'(-)'0.00\u00A4");
System.out.println(df.format(1.23456789));
System.out.println(df.format(-1.23456789));

получим (+)1,23руб. и (-)1,23руб.

Следующий вопрос который может возникнуть это как отформатировать число не только для текущей страны, но и для произвользой. Для этого используются DecimalFormatSymbols. Конструктор DecimalFormatSymbols принимает в качестве параметра локаль (Locale) для которой и будет выполняться форматирование. 

Задача: отформатировать число с точностью 2 знака после запятой и вывести как йены:
Код
DecimalFormatSymbols dfs = new DecimalFormatSymbols(Locale.JAPAN);
DecimalFormat df = new DecimalFormat("0.00 \u00A4\u00A4", dfs);
System.out.println(df.format(1.23456789));

получим 1.23 JPY.

Для облегчения жизни программиста в классе DecimalFormat есть несколько методов позволяющих получать форматы по умолчанию для валют - getCurrencyInstance(), для целых чисел - getIntegerInstance(), для процентов - getPercentInstance(), и формат общего назначения - getNumberInstance() или getInstance().

Задача: отформатировать число в денежном формате принятом в США:
Код
DecimalFormat df = (DecimalFormat) DecimalFormat.getCurrencyInstance(Locale.US);
System.out.println(df.format(12345.6789));
System.out.println(df.format( -12345.6789));

получим $12,345.68 и ($12,345.68).

Для преобразования из строки в число служит метод parce(). Число должно быть в том формате, что описан в шаблоне.

Задача: разобрать число в денежном формате:

Неправильно:
Код
DecimalFormat df = new DecimalFormat("0.0 \u00A4");
System.out.println(df.parse("12345.6789 $"));

получим java.text.ParseException: Unparseable number: "12345.6789 $".

Правильно:
Код
DecimalFormatSymbols dfs = new DecimalFormatSymbols(Locale.US);
DecimalFormat df = new DecimalFormat("0.0 \u00A4", dfs);
System.out.println(df.parse("12345.6789 $"));

получим 12345.6789.




Другой класс для форматирования чисел, это ChoiceFormat. Этот класс осуществляет форматирование следующим образом: в качестве аргумента конструктор принимает два массива, первый массив чисел {X1, X2, ... Xn}, второй массив строк {S1, S2, ... Sn}, при форматировании числа определяется к какому диапазону оно принадлежит ( x < X1 или Xi <= x < Xi+1 или Xn <= x) и выдатеся соответствующая ему строка Si. Числа в массиве должны идти строго по возрастанию.

Задача: по номеру дня недели получить его название:
Код
double[] days = {1, 2, 3, 4, 5, 6, 7};
String[] dayNames = {"Пн", "Вт", "Ср", "Чт", "Пт", "Сб", "Вс"};
ChoiceFormat cf = new ChoiceFormat(days, dayNames);
System.out.println(cf.format(2));

получим Вт.

Иногда бывает необходимо выделить случаи X=AX<AX>A, т.е. неравенства строгие, для этих целей в ChoiceFormat есть методы previousDouble() и nextDouble(). Они возвращают ближайшее к X слева и справа соответсвенно.

Задача: реализовать аналог функции SIGN:
Код
double[] limits = {ChoiceFormat.previousDouble(0.0), 0.0, ChoiceFormat.nextDouble(0.0)};
String[] formats = {"negative", "zero", "positive", };
ChoiceFormat cf = new ChoiceFormat(limits, formats);
System.out.println(cf.format( 0.00000000000000000000001));
System.out.println(cf.format( 0.0));
System.out.println(cf.format(-0.00000000000000000000001));

получим positivezeronegative.

Существует другой способ задания шаблонов для ChoiceFormat, это задать один текстовый шаблон в котором описать все границы диапазонов и значения для этих диапазонов в формате: <число>[#|<]<значение_для данного_диапазона>[|<следующий_шаблон>]. Формировать его вручную не очень удобно, проще получит автоматом из готовго форматера.

Задача: получить шаблон для функции SIGN:
Код
double[] limits = {ChoiceFormat.previousDouble(0.0), 0.0, ChoiceFormat.nextDouble(0.0)};
String[] formats = {"negative", "zero", "positive", };
ChoiceFormat cf = new ChoiceFormat(limits, formats);
System.out.println(cf.toPattern());

получим -4.9E-324#negative|0.0#zero|0.0<positive.

При парсинге ChoiceFormat определяет к какому диапазону принадлежит указанная строка и выдает число из этого диапазона (как правило левая граница диапазона).

Задача: получить номер дня недели по его имени:
Код
double[] days = {1, 2, 3, 4, 5, 6, 7};
String[] dayNames = {"Пн", "Вт", "Ср", "Чт", "Пт", "Сб", "Вс"};
cf = new ChoiceFormat(days, dayNames);
System.out.println(cf.parse("Ср"));

получим 3.0.




Для форматирования дат предназначен класс SimpleDateFormat, для полного понимания надо быть знакомым с классом java.util.Calendar. Как и DecimalFormat он основан на символьном шаблоне, в шаблоне применяются символы обозначающие те или иные поля класса Calendar, каждое поле определенное в Calendar имеет свое отображение в шаблоне. Все символы регистрозависимые и все латинские буквы зарезервированны, поэтому если вы хотите вставить какую нибудь букву в шаблон лучше ее брать в апострофы. Поля бывают нескольких видов:
  •  числовые - значением поля является число, если символ один, то не значищие нули не выводятся, если более то выводятся (столько сколько нулей сколько раз этот символ встречается), исключение составляет поле года, если символ y встречается меньше 3-х раз, то выводятся 2 цифры иначе действуют правила описанные выше
  •  символьные - значением поля является текст, для некоторых полей допускается сокращенная запись (дни недели и временные зоны), для других нет (ера, время суток и стандартная временная зона), выбор межде сокращенной и полной формой записи осуществляется исходя из количества символов (4 и более полная форма, менее сокращенная)
  •  смешанные - значением поля может быть как числом так и текстом, такое поле всего одно это месяц, если символ M встречается 2 или 1 раз, то это число в соответсвии с правилами из первого пункта. Если 3 раза, то это сокращенное, трехбуквенное название месяца. 4 и более полное название месяца.
Перечислим основные поля (полный список полей можно найти в документации): d - день месяца, M - месяц, y - год, H - часы (24-х часовая шкала), m - минуты, s - секунды.

Задача: вывести текущую дату в виде число название месяца полный год:
Код
SimpleDateFormat sdf = new SimpleDateFormat("d MMMM yyyy");
System.out.println(sdf.format(new Date()));

получаем 28 Сентябрь 2005.

Календарь используемый для форматирования можно установить с помощью setCalendar(), получить с помощью getCalendar().

Задача: получить текущую дату по буддийскому календарю:
Код
SimpleDateFormat sdf = new SimpleDateFormat("d MMMM yyyy");
sdf.setCalendar(new BuddhistCalendar());
System.out.println(sdf.format(new Date()));

получим 28 Сентябрь 2548.

Получаемые данные так же зависят от того какая локаль применяется для создания форматера, можно или задать DateFormatSymbols или указать локаль и тогда будут использованны умолчальные DateFormatSymbols для этой локали.
В классе SimpleDateFormat определенны методы для получения умолчальных шаблоны для конкретной локали, всего есть 3 основных метода getDateInstance() - получить форматер для даты, getTimeInstance() - получить форматер для времени, getDateTimeInstance()- получить форматер для даты-времени. Эти методы могут в качестве параметров принимать локаль для которой надо получить форматер и тип формата. Тип формата определяет насколько подробно будет отображена дата/время, есть четыре варианта (от самого подробного, к наименне подробному): FULL, LONG, MEDIUM, SHORT.

Задача: отформатировать текущую дату во всех формах, для русской локали:
Код
Locale ru = new Locale("ru");
DateFormat sdf = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL, ru);
System.out.println(sdf.format(new Date()));
sdf = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, ru);
System.out.println(sdf.format(new Date()));
sdf = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM, ru);
System.out.println(sdf.format(new Date()));
sdf = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, ru);
System.out.println(sdf.format(new Date()));

получим:
29 Сентябрь 2005 г. 0:07:31 MSD
29 Сентябрь 2005 г. 0:07:31 MSD
29.09.2005 0:07:31
29.09.05 0:07


Задача: получить названия месяцев для русской локали в правильной словоформе (не 29 Сентябрь, а 29 сентября):
Код
String[] months = {"января", "февраля", "марта", "апреля", "мая", "июня", "июля", "августа", "сентября", "октября", "ноября", "декабря"};
DateFormatSymbols dfs = new DateFormatSymbols(new Locale("ru"));
dfs.setMonths(months);
SimpleDateFormat sdf = new SimpleDateFormat("d MMMM yyyy 'г.'", dfs);
System.out.println(sdf.format(new Date()));

получим 29 сентября 2005 г.

Парсинг даты с помощью SimpleDateFormat происходит аналогично DecimalFormat. У Calendar будут установленны все поля которые присутствуют в шаблоне, все остальные поля будут сброшены в умолчальное значение.

Задача: перевести в дату время формирования файла с именем bakup_20050929_1836.rar
Код
SimpleDateFormat sdf = new SimpleDateFormat("'bakup_'yyyyMMdd_HHmm'.rar'");
Date date = sdf.parse("bakup_20050929_1836.rar");
System.out.println(date);

получим Thu Sep 29 18:36:00 MSD 2005.




И последний форматтер MessageFormatMessageFormat предназначен для формирования "читабельных" сообщений в программах. При создании MessageFormat в конструкторе указывается сообщение в котором содержатся символы подстановки (работает аналогично PrintStream.printf()). Подстановочные символы заключаются в фигурные скобки. При форматировании MessageFormat принимает в качестве аргументов массив объектов и подставляет их в нужное место. Если для переданного объекта нет соответсвующего подстановочного символа, то он игнорируется, и наоборот если для подстановочного символа не передан объект то оставляется как есть. Для преобразования объекта в строку использется или метод toString() или один из описанных ранее форматеров.

Формат подстановочного символа следующий: {0[,<тип_формата>[,<параметры_формата>]]}, в квадратных скобках указанны необязательные параметры. <тип_формата> - название форматтера используемого для форматирования, принимает следующие значения: numberdatetimechoice<параметры_формата> - параметры форматирования специфичные для каждого форматтера, для даты и времени можно указать либо один из стандартных шаблонов (shortmediumlongfull) либо непосредственно шаблон даты), аналогично для числа либо стандартный шаблон (integercurrencypercent) либо свой шаблон. Для choice указание шаблона обязательно. С помощью метода setFormat() можно установить форматер для определенного подстановочного символа вручную, полезно например для использования вложенных MessageFormat или собсвенных форматеров.

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

1. В формате предназначенном для записи в текстовый лог файл:
Код
Object[] param = {new Date(), new Integer(500), new Throwable().getStackTrace()[0], "Something happend"};
String pattern = "{0,date,yyyy-MM-dd HH:mm:ss.SSS}" +
                 " [{1,choice,300#FINEST|400#FINER|500#FINE|700#CONFIG|800#INFO|900#WARNING|1000#WARNING}]" +
                 " at {2}" +
                 " Message: {3}";
MessageFormat mf = new MessageFormat(pattern);
System.out.println(mf.format(param));

получим 2005-09-30 00:24:36.265 [FINE] at lsd.misc.FormatterTest.printMessageFormat(FormatterTest.java:66) Message: Something happend.

2. В формате XML
Код
Object[] param = {new Date(), new Integer(500), new Throwable().getStackTrace()[0], "Something happend"};
String pattern = "<LogRecord>\n" +
                 "  <time>{0,date,yyyy-MM-dd'T'HH:mm:ss.SSS}</time>\n" +
                 "  <level>{1,choice,300#FINEST|400#FINER|500#FINE|700#CONFIG|800#INFO|900#WARNING|1000#WARNING}</level>\n" +
                 "  <stack>{2}</stack>\n" +
                 "  <message>{3}</message>\n" +
                 "</LogRecord>\n";
MessageFormat mf = new MessageFormat(pattern);
System.out.println(mf.format(param));


получим
Код
<LogRecord>
  <time>2005-09-30T00:44:25.109</time>
  <level>FINE</level>
  <stack>lsd.misc.FormatterTest.printMessageFormat(FormatterTest.java:66)</stack>
  <message>Something happend</message>
</LogRecord>


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

Автор: LSD






Просмотров: 16772

 

 

Новые статьи:


Популярные:
  1. Как сделать цикличным проигрывание MIDI-файла?
  2. Создание AVI файла из рисунков
  3. Как устройство "отключить в данной конфигурации"?
  4. Kто в данный момент присоединен через Сеть?
  5. Как узнать количество доступной памяти?
  6. Как реализовать в RichEdit разноцветный текст?
  7. Как скрыть свое приложение от ProcessViewer
  8. Как программно нажать/скрыть/показ кнопку "Start"?
  9. Модуль работы с ресурсами в PE файлах
10. Функции вызова диалоговых окон выбора
11. Проверка граматики средствами Word'а из Delphi.
12. Модуль для упрощенного вызова сообщений
13. Функции для записи и чтение своих данных в, ЕХЕ- файле
14. Рекурсивный просмотр директорий
15. Network Traffic Monitor
16. Разные модули
17. Универсальная функция для обращения к любым экспортируем функциям DLL
18. Библиотека от VladS
19. Протектор для UPX'а
20. Еще об ICQ, сообщения по контакт листу?
21. Использование открытых интерфейсов
22. Теория и практика использования RTTI
23. Работа с TApplication
24. Примеры использования Drag and Drop для различных визуальных компонентов
25. Что такое порт? Правила для работы с портами
26. Симфония на клавиатуре
27. Загрузка DLL
28. Исправление автоинкремента
29. Взаимодействие с чужими окнами
30. Проверить дубляжи в столбце


 

 

 
 
На главную