Цикл статей «Учебник Java 8».
Следующая статья — «Java 8 консоль».
Предыдущая статья — «Java 8 дата и время».
В мире огромное количество различных языков, религий, культур и стран. В каждой стране и в каждом языке зачастую приняты свои формы записи чисел, дат и денежных единиц. Локализация приложения в конкретной стране — это далеко не самый тривиальный процесс, подразумевающий не только перевод на язык страны, но и запись чисел и дат в формате, принятом в этой стране, удаление иконок и изображений, нарушающих законодательство этой страны и т. д.
Например, в в русском языке принята следующая запись дат:
12.01.2016 — двенадцатое января две тысячи шестнадцатого года
10 июня 2016 — 10 июня 2016 года.
Но если мы локализуем приложение в США, то даты будут выглядеть так:
01/12/2016 — the twelfth of January year twenty sixteen
June 10, 2016 — the tenth of June year twenty sixteen
С записью чисел тоже всё далеко не так просто. Вот числа для русского языка:
10 000 000,34
3 454,456
А вот те же числа для США:
10,000,000.34
3,454.456
Все современные языки поддерживают конвертацию дат и числовых переменных в строку и обратно в соответствии с указанными региональными настройками (локалью) или региональными настройками по умолчанию.
Для понимания дальнейшего текста статьи рекомендуется ознакомиться с классом java.util.Locale, представляющего собой локаль (региональные настройки).
Вы можете получить экземпляр текущей локали с помощью кода:
1 |
Locale locale = Locale.getDefault() |
java.text.NumberFormat
Класс java.text.NumberFormat предназначен для форматирования и парсинга чисел. Это абстрактный класс, экземпляры которого можно получить с помощью методов getInstance():
1 |
public static final NumberFormat getInstance() |
1 |
public static NumberFormat getInstance(Locale inLocale) |
Полученный экземпляр NumberFormat можно использовать для форматирования чисел с помощью метода format и парсинга чисел с помощью метода parse:
1 |
public final String format(double number) |
1 |
public final String format(long number) |
1 2 |
public Number parse(String source) throws ParseException |
Пример:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
String str1 = java.text.NumberFormat.getInstance().format(10_000_000.34); String str2 = java.text.NumberFormat.getInstance().format(8000); String str3 = java.text.NumberFormat.getInstance().format(new java.math.BigDecimal("34000.56")); System.out.println(str1); System.out.println(str2); System.out.println(str3); try { Number var1 = java.text.NumberFormat.getInstance().parse(str1); Number var2 = java.text.NumberFormat.getInstance().parse(str2); Number var3 = java.text.NumberFormat.getInstance().parse(str3); System.out.println(var1); System.out.println(var2); System.out.println(var3); } catch (java.text.ParseException pe) { pe.printStackTrace(); } |
Результат:
1 2 3 4 5 6 |
10 000 000,34 8 000 34 000,56 1.000000034E7 8000 34000.56 |
java.text.DecimalFormat
Класс java.text.DecimalFormat расширяет класс java.text.NumberFormat. Класс DecimalFormat предназначен специально для работы с десятичными числами. Он поддерживает различные типы чисел: проценты, денежные единицы и т. д.
Все методы format и parse, способ создания аналогичны NumberFormat.
Класс DecimalFormat дополнительно содержит настройку:
1 |
public void setParseBigDecimal(boolean newValue) |
позволяющую методу parse возвращать экземпляры BigDecimal .
Класс java.text.DecimalFormat содержит два дополнительных конструктора, принимающих строку с форматом числа:
1 |
public DecimalFormat(String pattern) |
1 2 |
public DecimalFormat(String pattern, DecimalFormatSymbols symbols) |
Метод, принимающий DecimalFormatSymbols, позволяет полностью настроить форматирование числа. Строка pattern содержит шаблон вида "0.00" может содержать следующие специальные символы:
Спец. символ | Значение |
---|---|
Число | |
# | Число, незначащие нули не показываются |
. | Разделитель дробной и целой части |
- | Знак минуса |
, | Разделитель групп |
E | Разделяет мантиссу и экспоненту в научной записи. В префиксе или в суффиксе нужно заключать в кавычки. |
; | Разделяет положительный шаблон числа и отрицательный шаблон числа |
% | Умножается на 100 и показывается в процентах |
\u2030 | Умножается на 1 000 и показывается как в милях |
¤ (\u00A4) | Денежный знак. Заменяется денежный символом локали. Если задвоен, то указывается международный денежный символ. Если указан, то используется разделитель денег вместо разделителя дробной и целой части |
' | Кавычки. "'#'#" преобразует 123 в "#123" . Чтобы записать саму одинарную кавычку, используйте две одинарные кавычки. |
Пример:
1 |
System.out.println(new java.text.DecimalFormat("0000.000").format(10)); |
Результат:
1 |
0010,000 |
java.text.DateFormat
Класс java.text.DateFormat предназначен для форматирования дат. Получить экземпляр этого класса можно с помощью одного из методов:
1 |
public static final DateFormat getInstance() |
Если нужно форматировать/парсить только дату без времени, то нужно использовать один из следующих методов:
1 |
public static final DateFormat getDateInstance() |
1 |
public static final DateFormat getDateInstance(int style) |
1 2 |
public static final DateFormat getDateInstance(int style, Locale aLocale) |
Здесь style может быть одно из значений: DateFormat.FULL, DateFormat.LONG, DateFormat.SHORT, DateFormat.MEDIUM, DateFormat.DEFAULT.
Если нужно форматировать/парсить только время, то нужно использовать один из методов:
1 |
public static final DateFormat getTimeInstance() |
1 |
public static final DateFormat getTimeInstance(int style) |
1 2 |
public static final DateFormat getTimeInstance(int style, Locale aLocale) |
Здесь style может быть одно из значений: DateFormat.FULL, DateFormat.LONG, DateFormat.SHORT, DateFormat.MEDIUM, DateFormat.DEFAULT.
Если нужно форматировать/парсить дату с временем, то нужно использовать один из методов:
1 |
public static final DateFormat getDateTimeInstance() |
1 2 |
public static final DateFormat getDateTimeInstance(int dateStyle, int timeStyle) |
1 2 3 |
public static final DateFormat getDateTimeInstance(int dateStyle, int timeStyle, Locale aLocale) |
Форматирование и парсинг также происходит с помощью методов format и parse . Пример:
1 |
System.out.println(java.text.DateFormat.getInstance().format(new java.util.Date()); |
java.text.SimpleDateFormat
Класс java.text.SimpleDateFormat наследуется от java.text.DateFormat и позволяет указать пользовательский шаблон форматирования.
Конструкторы:
1 |
public SimpleDateFormat(String pattern) |
1 2 |
public SimpleDateFormat(String pattern, Locale locale) |
1 2 |
public SimpleDateFormat(String pattern, DateFormatSymbols formatSymbols) |
Конструктор с DateFormatSymbols позволяет создать форматировщик, используя особые правила.
Шаблон pattern может содержать следующие специальные символы:
Буква | Компонент даты и времени | Представление | Примеры |
---|---|---|---|
G |
Эра | Text | AD |
y |
Год | Year | 1996 ; 96 |
Y |
Год | Year | 2009 ; 09 |
M |
Месяц в году (зависит от контекста) | Month | July ; Jul ; 07 |
L |
Месяц в году (самостоятельная форма) | Month | July ; Jul ; 07 |
w |
Неделя в годе | Number | 27 |
W |
Неделя в месяце | Number | 2 |
D |
День в году | Number | 189 |
d |
День в месяце | Number | 10 |
F |
День недели в месяце | Number | 2 |
E |
Название дня недели | Text | Tuesday ; Tue |
u |
Номер дня недели (1 = Понедельник, …, 7 = Воскресенье) | Number | 1 |
a |
Am/pm | Text | PM |
H |
Час в дне (0-23) | Number | 0 |
k |
Час в дне (1-24) | Number | 24 |
K |
Час в дне am/pm (0-11) | Number | 0 |
h |
Час в дне am/pm (1-12) | Number | 12 |
m |
Минуты | Number | 30 |
s |
Секунды | Number | 55 |
S |
Миллисекунды | Number | 978 |
z |
Часовой пояс | General time zone | Pacific Standard Time ; PST ; GMT-08:00 |
Z |
Часовой пояс | RFC 822 time zone | -0800 |
X |
Часовой пояс | ISO 8601 time zone | -08 ; -0800 ; -08:00 |
Описание столбца «представление»:
- Text: Если в шаблоне 4 буквы или более, то используется полная форма, в противном случае используется сокращённая форма. При парсинге принимаются обе формы, независимо от количества букв в шаблоне.
- Number: Количество букв в шаблоне — это минимальное количество цифр, более короткие числа добиваются нулями. При парсинге количество букв игнорируется, если только оно не требуется для разделения соседних полей.
- Year: Если Calendar форматировщика является григорианским календарём, то применяются следующие правила. При форматировании если количество букв равно двум, то год усекается до двух цифр, в противном случае интерпретируется как число. При парсинге если количество букв больше двух, то год интерпретируется буквально, независимо от количества цифр. Поэтому использование шаблона "MM/dd/yyyy" и строки "01/11/12" получается 11 января 12 года нашей эры. При парсинге с сокращённой формой года ( "y" или "yy" ) SimpleDateFormat интерпретирует сокращённый год относительно какого-либо века. Он выравнивает даты так, чтобы они были в диапазоне от 80 лет до даты создания SimpleDateFormat и до 20 лет после даты создания SimpleDateFormat. Во время парсинга только строки, состоящие строго из двух цифр интерпретируются в текущий век. Любые другие числовые строки, состоящие из одной цифры или трёх и более, интерпретируются как полный год.
- Month: Если количество букв в шаблоне равно 3 или более, то месяц интерпретируется как текст, в противном случае интерпретируется как число. Буква M создаёт имена месяцев, зависимые от контекста. Если в конструктор был передан DateFormatSymbols или был использован метод setDateFormatSymbols, то имена месяцев берутся из DateFormatSymbols. Буква L создаёт самостоятельную форму имён месяцев.
- /a>General time zone: Часовые пояса интерпретируются по текстовым именам. При использовании смещения часовой пояс указывается в виде GMT +01:30 или GMT-12:33.
- RFC 822 time zone: Используются четыре цифры: -0800 или +1200.
- ISO 8601 time zone: Используются две цифры, четыре цифры, или с разделением часов и минут двоеточием: -08; -0800; -08:00.
java.io.PrintStream
Класс java.io.PrintStream позволяет писать форматированные данные в любой поток. Вам вряд ли когда-нибудь придётся создавать экземпляры этого класса вручную, гораздо чаще вы будете использовать готовые классы, вроде возвращаемых System.out. PrintStream имеет методы print и println, перегруженные для любого примитивного типа и для класса Object (в этом случае используется его метод toString() ).
Класс PrintStream никогда не бросает IOException, вместо этого он устанавливает свой внутренний флаг, который может быть проверен с помощью метода public boolean checkError().
Особое внимание заслуживают методы:
1 2 |
public PrintStream format(String format, Object... args) |
1 2 3 |
public PrintStream format(Locale l, String format, Object... args) |
1 2 3 |
public PrintStream printf(Locale l, String format, Object... args) |
1 2 |
public PrintStream printf(String format, Object... args) |
Эти методы позволяют писать в поток форматированные данные. Здесь format — это шаблон строки, который подробно в пункте «java.util.Formatter».
java.util.Formatter
Класс java.util.Formatter используется во всех методах, принимающих строку форматирования: java.io.PrintStream.format , System.out.format, String.format и т. д.
Каждый метод, принимающий строку форматирования, нуждается в шаблоне и списке аргументов. Пример:
1 2 |
Calendar c = ...; String s = String.format("Duke's Birthday: %1$tm %1$te,%1$tY", c); |
Строка форматирования является первым аргументом метода format. Она содержит три спецификатора формата: "%1$tm", "%1$te" и "%1$tY", которые указывают на способ обработки аргументов, и как они будут вставлены в текст. Остальные части строки содержат фиксированный текст, включающий в себя "Dukes Birthday: " и любые другие пробелы и знаки препинания. Список аргументов состоит из всех аргументов, переданных в метод после строки форматирования. В примере выше список аргументов содержит только один объект java.util.Calendar.
Спецификаторы формата для общих, символьных и числовых типов имеют следующий синтаксис:
1 |
%[argument_index$][flags][width][.precision]conversion |
Необязательный argument_index показывает позицию аргумента в списке аргументов. К первому аргументу ссылаются "1$", ко второму "2$", нумерация аргументов начинается с единицы.
Необязательный flags — это набор символов, модифицирующих выходной формат. Допустимый набор символов зависит от conversion.
Необязательный width — это положительное десятичное число, указывающее минимальное количество символов, которое будет записано в выходную строку.
Необязательный precision — это неотрицательное десятичное число, обычно используемое для ограничения количества символов. Поведение precision зависит от conversion.
Обязательное conversion — это символ, указывающий на способ форматирования аргумента. Допустимый набор символов зависит от типа аргумента.
Спецификаторы форматов, используемые для дат и времени, имеют следующий синтаксис:
1 |
%[flags][width]conversion |
Бывают следующие виды conversion:
- General — может быть применён к любому типу аргумента.
- Character — может быть применён к базовым типам, представляющим символы Юникода: char, Character, byte, Byte, short, Short. Этот тип conversion может быть также применён к типам int и Integer, если Character.isValidCodePoint(int) возвращает true.
- Integral — может быть применён к любому целочисленному типу Java: byte, Byte, short, Short, int, Integer, long, Long, BigInteger (но не к char или Character).
- Floating point — может быть применён к типам с плавающей точкой: float, Float, double, Double и BigDecimal.
- Date/Time — может быть применён к типам Java, которые могут содержать дату или время: long, Long, Calendar, Date или TemporalAccessor.
- Percent — создаёт литеру '%' ( '\u0025' ).
- Line separator — специфичный для платформы разделитель строк.
Следующая таблица содержит поддерживаемые conversion. Заглавные буквы имеют то же значение, что и прописные, но результат преобразуется в верхнему регистру с помощью String.toUpperCase().
Conversion | Категория аргумента | Описание |
---|---|---|
'b', 'B' | general | Если аргумент
null, то результат
"false". Если аргумент
boolean или
Boolean , то результатом будет результат вызова
String.valueOf(arg). В противном случае результат
"true". |
'h', 'H' | general | Если аргумент
null , то результатом будет
"null". В противном случае результат получается вызовом
Integer.toHexString(arg.hashCode()). |
's', 'S' | general | Если аргумент null, то результат "null". Если аргумент реализует интерфейс java.util.Formattable, то вызывается метод arg.formatTo. В противном случае результат получается вызовом arg.toString(). |
'c', 'C' | character | Результатом будет символ юникода. |
'd' |
integral | Результат форматируется как десятичное целое. |
'o' |
integral | Результат форматируется как восьмиричное целое. |
'x', 'X' | integral | Результат форматируется как шестнадцатеричное целое. |
'e', 'E' | floating point | Результат форматируется как десятичное число в научной записи. |
'f' |
floating point | Результат форматируется как десятичное число. |
'g', 'G' | floating point | Результат форматируется используя научную запись или десятичный формат в зависимости от точости и значения после округления. |
'a', 'A' | floating point | Результат форматируется в шестнадцатеричное число с плавающей точкой, мантиссой и показателем степени. Не поддерживается для java.math.BigDecimal. |
't', 'T' | date/time | Префикс для даты и времени. |
'%' |
percent | Результатом будет символ '%' ( '\u0025' ) |
'n' |
line separator | Разделитель линий, принятой в текущей платформе. |
Следующие символы используются в качестве суффиксов к 't' и 'T' и используются для дат и времени:
Для форматирования времени:
'H' |
Час в 24-часовом дне. Форматируется как две цифры с предваряющим нулём, если нужно, то есть 00 - 23 . |
'I' |
Час от единицы до двенадцати. Форматируется как две цифры с предваряющим нулём, если нужно, то есть 01 - 12 . |
'k' |
Час в 24-часовом дне, 0 - 23 . |
'l' |
Час от единицы до двенадцати, 1 - 12 . |
'M' |
Минуты в часе. Форматируются как две цифры с предваряющим нулём, если нужно, то есть 00 - 59 . |
'S' |
Секунды в минуте, форматируются как две цифры с предваряющим нулём, если нужно, то есть 00 - 60 (
"60" — специальное значение, необходимое для поддержки секунды координации). |
'L' |
Миллисекунды в секунде. Форматируются как три цифры с предваряющими нулями, если нужно, то есть 000 - 999 . |
'N' |
Наносекунды в секунде. Форматируются как девять цифр с предваряющими нулями, если нужно, то есть 000000000 - 999999999 . |
'p' |
До обеда или поле обеда ( "am" или "pm" ). Используйте префикс 'T', чтобы результат был в верхнем регистре. |
'z' |
RFC 822 смещение часовой зоны от GMT, например -0800 . Это значение выравнивается для учёта перехода на зимнее/летнее время. Для
long,
Long,
Date используется часовой пояс по умолчанию для текущего экземпляра виртуальной машины Java. |
'Z' |
Строка, содержащая абревиатуру часового пояса. Это значение выравнивается, чтобы учеть переход на зимнее/летнее время. Для long, Long, Date используется часовой пояс по умолчанию для текущего экземпляра виртуальной машины Java. |
's' |
Секунды с начала эпохи от 1 января 1970 года 00:00:00 UTC. От Long.MIN_VALUE/1000 до Long.MAX_VALUE/1000. |
'Q' |
Миллисекунды с начала эпохи 1 января 1970 года 00:00:00 UTC. От Long.MIN_VALUE до Long.MAX_VALUE . |
Для форматирования дат:
'B' |
Полное имя месяца в соответствии с региональными настройками, например "январь", "февраль", "January", "February". |
'b' |
Сокращённое имя месяца в соответствии с региональными настройками, например "янв", "фев", "Jan", "Feb". |
'h' |
То же что и 'b'. |
'A' |
Полное имя дня недели в соответствии с региональными настройками, например
"воскресенье",
"понедельник",
"Sunday",
"Monday". |
'a' |
Короткое название дня недели в соответствии с региональными настройками, например "Пн", "Вт", "Sun", "Mon". |
'C' |
Четыре цифры года, поделённые на 100, и форматированные как две цифры с предваряющим нулём, если нужно 00 - 99. |
'Y' |
Год, форматированный как минимум четырьмя цифрами с предваряющим нулём, если нужно, то есть 0092 равен 92 CE для григорианского календаря. |
'y' |
Последние две цифры года, форматированные с предваряющим нулём, если необходимо, то есть 00 - 99 . |
'j' |
День года, форматированный как три цифры с предваряющими нулями, если необходимо, то есть 001 - 366 для григорианского календаря. |
'm' |
Месяц, форматированный двумя цифрами с предваряющим нулём, если необходимо, то есть 01 - 13 . |
'd' |
День месяца, форматированный как две цифры с предваряющими нулями, если необходимо, то есть 01 - 31 |
'e' |
День в месяце, форматированный как две цифры, то есть 1 - 31 . |
Для форматирования наиболее часто используемых сочетаний дат и времени:
'R' |
Время 24-часовой день "%tH:%tM" |
'T' |
Время в формате "%tH:%tM:%tS". |
'r' |
Время в формате "%tI:%tM:%tS %Tp". |
'D' |
Дата в формате "%tm/%td/%ty". |
'F' |
ISO 8601 форматированная дата "%tY-%tm-%td". |
'c' |
Дата и время в формате "%ta %tb %td %tT %tZ %tY". |
Поддерживаемые флаги:
Флаг | General | Character | Integral | Floating Point | Date/Time | Описание |
---|---|---|---|---|---|---|
'-' | y | y | y | y | y | Результат будет выровнен по левому краю. |
'#' | y1 | — | y3 | y | — | Результат должен использовать альтернативну форму, зависимую от conversion. |
'+' | — | — | y4 | y | — | Результат всегда будет содержать знак. |
' ' | — | — | y4 | y | — | Результат будет содержать лидирующий пробел для положительых значений. |
'0' | — | — | y | y | — | Результа будет выровнен нулями. |
',' | — | — | y2 | y5 | — | Результат будет содержать разделители групп указанной локали. |
'(' | — | — | y4 | y5 | — | Результат будет заключать отрицательные значения в скобки. |
1 Зависит от определения Formattable
.
2 Только для conversion 'd' .
3 Только для conversion 'o', 'x' и 'X'.
4 Для conversion
'd',
'o',
'x' и
'X', примененных к java.math.BigInteger, или
'd' , применённой к
byte,
Byte,
short,
Short,
int,
Integer,
long,
Long.
5 Только для conversion 'e' , 'E' , 'f' , 'g' и 'G'.
width указывает минимальное количество символов, которые будут записаны в выходную строку. Для разделителя линий width не применяется.
precision указывает максимальное количество символов, которые будут записаны в выходную строку. Для conversion 'a', 'A', 'e', 'E', 'f' precision — количество символов после точки. Если conversion равен 'g' или 'G', то precision указывает общее количество значимых чисел после округления. Для character, integral, date/time, percent, line separator этот параметр не указывается.
argument index — десятичное число, указывающее позицию аргумента в списке аргументов. Первый аргумент "1$", второй "2$" и т. д. Можно использовать флаг '<' ( '\u003c' ), который использует ещё раз предыдущий аргумент:
1 2 3 4 |
Calendar c = ...; String s1 = String.format("Duke's Birthday: %1$tm %1$te,%1$tY", c); String s2 = String.format("Duke's Birthday: %1$tm %<te,%<tY", c); |
java.util.Scanner
Класс java.util.Scanner предназначен для разбиения форматированного ввода на токены и конвертирования токенов в соответствующий тип данных.
По умолчанию сканер использует пробельные символы (пробелы, табуляторы, разделители линий) для разделения токенов. Рассмотрите следующий код:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
import java.io.*; import java.util.Scanner; public class ScanXan { public static void main(String[] args) throws IOException { Scanner s = null; try { s = new Scanner(new BufferedReader(new FileReader("xanadu.txt"))); while (s.hasNext()) { System.out.println(s.next()); } } finally { if (s != null) { s.close(); } } } } |
Если файл «xanadu.txt» содержит следующий текст:
1 2 3 4 5 |
In Xanadu did Kubla Khan A stately pleasure-dome decree: Where Alph, the sacred river, ran Through caverns measureless to man Down to a sunless sea. |
То результатом работы программы будет вывод:
1 2 3 4 5 6 7 8 9 |
In Xanadu did Kubla Khan A stately pleasure-dome ... |
Чтобы использовать другой разделитель токенов используйте метод useDelimiter, в который передаётся регулярное выражение. Например, предположим, что мы хотим использовать в качестве разделителя запятую, после которой может идти, а может не идти пробел:
1 |
s.useDelimiter(",\\s*"); |
Класс java.util.Scanner поддерживает все примитивные типы Java, java.math.BigInteger и java.math.BigDecimal. Scanner использует экземпляр java.util.Locale для преобразования строк в эти типы данных. Пример:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
import java.io.FileReader; import java.io.BufferedReader; import java.io.IOException; import java.util.Scanner; import java.util.Locale; public class ScanSum { public static void main(String[] args) throws IOException { Scanner s = null; double sum = 0; try { s = new Scanner(new BufferedReader(new FileReader("usnumbers.txt"))); s.useLocale(Locale.US); while (s.hasNext()) { if (s.hasNextDouble()) { sum += s.nextDouble(); } else { s.next(); } } } finally { s.close(); } System.out.println(sum); } } |
Цикл статей «Учебник Java 8».
Следующая статья — «Java 8 консоль».
Предыдущая статья — «Java 8 дата и время».