Форматирование чисел в Java: DecimalFormat

https://dzone.com/articles/java-numeric-formatting-decimalformat
  • Перевод
Всем привет! Что ж, курс «Разработчик Java» в своём новом формате вполне себе стартует сегодня, мы готовим теперь к следующему запуску новый формат полезностей, которые будут делиться на разные уровни и дорабатываться под разные запросы. А пока глянем на то что у нас осталось из наших запасов и посмотрим на использование DecimalFormat для контроля представления чисел

Поехали.

Весь код, предоставленный ниже, показывает, что инстансы, которые возвращает “getInstance” NumberFormat, на самом деле являются инстансами DecimalFormat. От аналогичных инстансов класса DecimalFormat их отличают настройки атрибутов, например, минимум/максимум целых значений (слева от десятичной точки) и минимум/максимум дробных значений (справа от десятичной точки). Все они имеют одинаковый режим округления и настройки валют.



Инстансы, предоставленные NumberFormat.getInstance() — это инстансы DecimalFormat

/**
* Выдать характеристики предоставленного объекта Currency 
* в стандартной форме вывода.
*
* @param currency Инстанс Currency, атрибуты которого
*    будут выданы в стандартной форме вывода
*/
public void printCurrencyCharacteristics(final Currency currency)
{
  out.print("\tCurrency: " + currency.getCurrencyCode()
     + "(ISO 4217 Code: " + currency.getNumericCode() + "), ");
  out.println(currency.getSymbol() + ", (" + currency.getDisplayName() + ")");
}
/**
* Выдать характеристики предоставленного инстанса
* в стандартной форме вывода под заголовком, включающим следующее 
* описание.
*
* @param numberFormat Инстанс NumberFormat, чьи ключевые
*    характеристики должны быть написаны в стандартной форме вывода.
* @param description Описание, которое должно быть включено в 
*    форму вывода.
*/
public void printNumberFormatCharacteristics(
  final NumberFormat numberFormat, final String description)
{
  out.println(description + ": " + numberFormat.getClass().getCanonicalName());
  out.println("\tRounding Mode:           " + numberFormat.getRoundingMode());
  out.println("\tMinimum Fraction Digits: " + numberFormat.getMinimumFractionDigits());
  out.println("\tMaximum Fraction Digits: " + numberFormat.getMaximumFractionDigits());
  out.println("\tMinimum Integer Digits:  " + numberFormat.getMinimumIntegerDigits());
  out.println("\tMaximum Integer Digits:  " + numberFormat.getMaximumIntegerDigits());
  printCurrencyCharacteristics(numberFormat.getCurrency());
  if (numberFormat instanceof DecimalFormat)
  {
     final DecimalFormat decimalFormat = (DecimalFormat) numberFormat;
     out.println("\tPattern: " + decimalFormat.toPattern());
  }
}
/**
* Отобразить ключевые характеристики “стандарта”
* NumberFormat/DecimalFormat инстансов, возвращенных статическими
* NumberFormat методами getIntegerInstance(), getCurrencyInstance(),
* getPercentInstance() и getNumberInstance().
*/
public void demonstrateDecimalFormatInstancesFromStaticNumberFormatMethods()
{
  final NumberFormat integerInstance = NumberFormat.getIntegerInstance();
  printNumberFormatCharacteristics(integerInstance, "IntegerInstance");
  final NumberFormat currencyInstance = NumberFormat.getCurrencyInstance();
  printNumberFormatCharacteristics(currencyInstance, "CurrencyInstance");
  final NumberFormat percentInstance = NumberFormat.getPercentInstance();
  printNumberFormatCharacteristics(percentInstance, "PercentInstance");
  final NumberFormat numberInstance = NumberFormat.getNumberInstance();
  printNumberFormatCharacteristics(numberInstance, "NumberInstance");
}




Хотя мой прошлый пост и все начало этого поста демонстрируют получение инстансов DecimalFormat через статические методы NumberFormat, в DecimalFormat есть три перегруженных конструктора DecimalFormat(), DecimalFormat(String) и DecimalFormat(String, DecimalFormatSymbols). Тем не менее, стоит отметить, что в документации DecimalFormat's Javadoc есть предупреждение: “Как правило, не стоит вызывать DecimalFormat конструкторы напрямую, так как фабричные методы NumberFormat могут возвращать подклассы, отличные от DecimalFormat.” Несмотря на предупреждение Javadoc мои следующие примеры иллюстрируют инстансы DecimalFormat их прямыми конструкторами. В нашем случае нет никаких причин этого не делать.

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

/**
* Применить предоставленный паттерн к инстансу DecimalFormat и написать
* результат в стандартной форме вывода вместе с представленным описанием.
* 
*
* @param pattern Паттерн будет применен к инстансу Decimal Format
* @param description Описание применяемого паттерна.
*/
private void applyPatternToStandardSample(
  final String pattern, final String description)
{
  final DecimalFormat decimalFormat = new DecimalFormat(pattern);
  printHeader(description + " - Applying Pattern '" + pattern + "'");
  for (final double theDouble : ourStandardSample)
  {
     out.println(
        theDouble + ": " + decimalFormat.format(theDouble));
  }
}
/**
* Продемонстрировать различные string-based паттерны, примененные к  
* инстансам DecimalFormat.
*/
public void demonstrateDecimalFormatPatternStringConstructor()
{
  final String sixFixedDigitsPattern = "000000";
  applyPatternToStandardSample(sixFixedDigitsPattern, "Six Fixed Digits");
  final String sixDigitsPattern = "###000";
  applyPatternToStandardSample(sixDigitsPattern, "Six Digits Leading Zeros Not Displayed");
  final String percentagePattern = "";
  applyPatternToStandardSample(percentagePattern, "Percentage");
  final String millePattern = "\u203000";
  applyPatternToStandardSample(millePattern, "Mille");
  final String currencyPattern = "\u00A4";
  applyPatternToStandardSample(currencyPattern, "Currency");
  final String internationalCurrencyPattern = "\u00A4";
  applyPatternToStandardSample(internationalCurrencyPattern, "Double Currency");
  final String scientificNotationPattern = "0.###E0";
  applyPatternToStandardSample(scientificNotationPattern, "Scientific Notation");
}

==================================================================
= Six Fixed Digits - Applying Pattern '000000'
==================================================================
NaN: �
0.25: 000000
0.4: 000000
0.567: 000001
1.0: 000001
10.0: 000010
100.0: 000100
1000.0: 001000
10000.0: 010000
100000.0: 100000
1000000.0: 1000000
1.0E7: 10000000
Infinity: ∞
==================================================================
= Six Digits Leading Zeros Not Displayed - Applying Pattern '###000'
==================================================================
NaN: �
0.25: 000
0.4: 000
0.567: 001
1.0: 001
10.0: 010
100.0: 100
1000.0: 1000
10000.0: 10000
100000.0: 100000
1000000.0: 1000000
1.0E7: 10000000
Infinity: ∞
==================================================================
= Percentage - Applying Pattern ''
==================================================================
NaN: �
0.25: %25
0.4: %40
0.567: %57
1.0: %100
10.0: %1000
100.0: %10000
1000.0: %100000
10000.0: %1000000
100000.0: %10000000
1000000.0: %100000000
1.0E7: %1000000000
Infinity: %∞
==================================================================
= Mille - Applying Pattern '‰00'
==================================================================
NaN: �
0.25: ‰250
0.4: ‰400
0.567: ‰567
1.0: ‰1000
10.0: ‰10000
100.0: ‰100000
1000.0: ‰1000000
10000.0: ‰10000000
100000.0: ‰100000000
1000000.0: ‰1000000000
1.0E7: ‰10000000000
Infinity: ‰∞
==================================================================
= Currency - Applying Pattern '¤'
==================================================================
NaN: �
0.25: $0
0.4: $0
0.567: $1
1.0: $1
10.0: $10
100.0: $100
1000.0: $1000
10000.0: $10000
100000.0: $100000
1000000.0: $1000000
1.0E7: $10000000
Infinity: $∞
==================================================================
= Double Currency - Applying Pattern '¤'
==================================================================
NaN: �
0.25: $0
0.4: $0
0.567: $1
1.0: $1
10.0: $10
100.0: $100
1000.0: $1000
10000.0: $10000
100000.0: $100000
1000000.0: $1000000
1.0E7: $10000000
Infinity: $∞
==================================================================
= Scientific Notation - Applying Pattern '0.###E0'
==================================================================
NaN: �
0.25: 2.5E-1
0.4: 4E-1
0.567: 5.67E-1
1.0: 1E0
10.0: 1E1
100.0: 1E2
1000.0: 1E3
10000.0: 1E4
100000.0: 1E5
1000000.0: 1E6
1.0E7: 1E7
Infinity: ∞

В последних двух примерах применения DecimalFormat я получаю инстанс DecimalFormat при помощи NumberFormat.getInstance(Locale). Первый фрагмент кода показывает различные локализации, примененные к одному числу двойной точности, и формат вывода для каждой из них.

/**
* Выдать инстанс DecimalFormat на базе локального инстанса
*
* @param locale Локаль ассоциируется с предоставленным инстансом
*  DecimalFormat.
* @return Инстанс DecimalFormat, ассоциированный с предоставленной локализацией.
* @throws ClassCastException Thrown, если объект, предоставленный мне 
*   NumberFormat.getCurrencyInstance(Locale) НЕ является инстансом 
*    класса {@link java.text.DecimalFormat}.
*/
private DecimalFormat getDecimalFormatWithSpecifiedLocale(final Locale locale)
{
  final NumberFormat numberFormat = NumberFormat.getCurrencyInstance(locale);
  if (!(numberFormat instanceof DecimalFormat))
  {
     throw new ClassCastException(
        "NumberFormat.getCurrencyInstance(Locale) returned an object of type "
           + numberFormat.getClass().getCanonicalName() + " instead of DecimalFormat.");
  }
  return (DecimalFormat) numberFormat;
}
/**
* Демонстрация форматирования чисел двойной точности с различными локализациями.
*/
public void demonstrateDifferentLocalesCurrencies()
{
  final double monetaryAmount = 14.99;
  out.println("Locale-specific currency representations of " + monetaryAmount + ":");
  out.println("\tLocale.US:            "
     + getDecimalFormatWithSpecifiedLocale(Locale.US).format(monetaryAmount));
  out.println("\tLocale.UK:            "
     + getDecimalFormatWithSpecifiedLocale(Locale.UK).format(monetaryAmount));
  out.println("\tLocale.ENGLISH:       "
     + getDecimalFormatWithSpecifiedLocale(Locale.ENGLISH).format(monetaryAmount));
  out.println("\tLocale.JAPAN:         "
     + getDecimalFormatWithSpecifiedLocale(Locale.JAPAN).format(monetaryAmount));
  out.println("\tLocale.GERMANY:       "
     + getDecimalFormatWithSpecifiedLocale(Locale.GERMANY).format(monetaryAmount));
  out.println("\tLocale.CANADA:        "
     + getDecimalFormatWithSpecifiedLocale(Locale.CANADA).format(monetaryAmount));
  out.println("\tLocale.CANADA_FRENCH: "
     + getDecimalFormatWithSpecifiedLocale(Locale.CANADA_FRENCH).format(monetaryAmount));
  out.println("\tLocale.ITALY:         "
     + getDecimalFormatWithSpecifiedLocale(Locale.ITALY).format(monetaryAmount));
}

Региональные валютные представления 14.99:

Locale.US:            $14.99
Locale.UK:            £14.99
Locale.ENGLISH:       ¤14.99
Locale.JAPAN:         ¥15
Locale.GERMANY:       14,99 €
Locale.CANADA:        $14.99
Locale.CANADA_FRENCH: 14,99 $
Locale.ITALY:         € 14,99

Все примеры DecimalFormat до этого момента фокусировались на форматировании чисел для презентации. Финальный пример идет в обратном направлении и парсит значение из строкового представления:

/**
* Демонстрация парсинга
*/
public void demonstrateParsing()
{
  final NumberFormat numberFormat = NumberFormat.getCurrencyInstance(Locale.US);
  final double value = 23.23;
  final String currencyRepresentation = numberFormat.format(value);
  out.println("Currency representation of " + value + " is " + currencyRepresentation);
  try
  {
     final Number parsedValue = numberFormat.parse(currencyRepresentation);
     out.println("Parsed value of currency representation " + currencyRepresentation + " is " + parsedValue);
  }
  catch (ParseException parseException)
  {
     out.println("Exception parsing " + currencyRepresentation + parseException);
  }
}

Currency representation of 23.23 is $23.23
Parsed value of currency representation $23.23 is 23.23

В последнем примере не было необходимости в доступе к конкретным методам DecimalNumbers, достаточно было использования NumberFormat методов. Валюты форматируются с помощью NumberFormat.format(double), а затем парсятся до оригинального значения с NumberFormat.parse(String).

NumberFormat, а точнее DoubleFormat, “форматируют и парсят значения для любой локализации.”
Отус 292,88
Профессиональные онлайн-курсы для разработчиков
Поделиться публикацией
Комментарии 1
    0
    Очень не читабельно, закалабсите код

    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

    Самое читаемое