Практическо ръководство за десетичен формат

1. Общ преглед

В тази статия ще изследваме класа DecimalFormat заедно с практическите му приложения.

Това е подклас на NumberFormat , което позволява форматиране десетични числа " String представяне използвайки предварително определени модели.

Може да се използва и обратно, за да се анализира низовете на числа.

2. Как работи?

За да форматираме число, трябва да дефинираме шаблон, който представлява последователност от специални знаци, потенциално смесени с текст.

Има 11 знака със специален шаблон, но най-важните са:

  • 0 - отпечатва цифра, ако е предвидена, 0 в противен случай
  • # - отпечатва цифра, ако е предоставена, нищо друго
  • . - посочете къде да поставите десетичния разделител
  • , - посочете къде да поставите разделителя за групиране

Когато шаблонът се приложи към число, неговите правила за форматиране се изпълняват и резултатът се отпечатва според символа DecimalFormatSymbol на нашия JVM Locale, освен ако не е посочен конкретен Locale .

Изходи Следващите примери "са от JVM работи на английски локал .

3. Основно форматиране

Нека сега видим кои изходи се получават при форматиране на същия номер със следните модели.

3.1. Обикновени десетични знаци

double d = 1234567.89; assertThat( new DecimalFormat("#.##").format(d)).isEqualTo("1234567.89"); assertThat( new DecimalFormat("0.00").format(d)).isEqualTo("1234567.89"); 

Както виждаме, целочислената част никога не се изхвърля, без значение дали моделът е по-малък от броя.

assertThat(new DecimalFormat("#########.###").format(d)) .isEqualTo("1234567.89"); assertThat(new DecimalFormat("000000000.000").format(d)) .isEqualTo("001234567.890"); 

Ако шаблонът вместо това е по-голям от числото, нулите се добавят, докато хешовете се изпускат, както в цялото число, така и в десетичните части.

3.2. Закръгляване

Ако десетичната част на шаблона не може да съдържа цялата точност на въведеното число, той се закръглява.

Тук частта .89 е закръглена до .90, след което 0 е отпаднала:

assertThat(new DecimalFormat("#.#").format(d)) .isEqualTo("1234567.9"); 

Тук частта .89 е закръглена до 1.00, след това .00 е отпаднала и 1 е сумирано до 7:

assertThat(new DecimalFormat("#").format(d)) .isEqualTo("1234568"); 

Режимът на закръгляне по подразбиране е HALF_EVEN, но може да бъде персонализиран чрез метода setRoundingMode .

3.3. Групиране

Разделителят за групиране се използва за задаване на подмодел, който се повтаря автоматично:

assertThat(new DecimalFormat("#,###.#").format(d)) .isEqualTo("1,234,567.9"); assertThat(new DecimalFormat("#,###").format(d)) .isEqualTo("1,234,568"); 

3.4. Множество модели за групиране

Някои държави имат променлив брой модели на групиране в своите номерационни системи.

Индийската система за номериране използва формата #, ##, ###. ##, в който само първият разделител за групиране съдържа три числа, докато всички останали съдържат две числа.

Това не е възможно да се постигне с помощта на класа DecimalFormat , който запазва само най-новия модел, срещан отляво надясно, и го прилага към цялото число, пренебрегвайки предишните модели на групиране.

Опит за използване на шаблон #, ##, ##, ##, ### ще доведе до прегрупиране към #######, ### и ще завърши с преразпределение до #, ###, # ##, ###.

За да постигнете съвпадение на множество групиращи модели, е необходимо да напишете собствен код за манипулиране на String или алтернативно да опитаме DecimalFormat на Icu4J , който позволява това.

3.5. Смесване на струнни литерали

Възможно е да се смесват String литерали в шаблона:

assertThat(new DecimalFormat("The # number") .format(d)) .isEqualTo("The 1234568 number"); 

Също така е възможно да се използват специални символи като Stral литерали, чрез избягване:

assertThat(new DecimalFormat("The '#' # number") .format(d)) .isEqualTo("The # 1234568 number"); 

4. Локализирано форматиране

Много страни не използват английски символи и използват запетая като десетичен разделител, а точката като разделител за групиране.

Изпълнението на шаблона #, ###. ## на JVM с италиански Locale , например, ще изведе 1.234.567,89.

Въпреки че това може да бъде полезна функция i18n в някои случаи, в други може да се наложи да приложим специфичен, независим от JVM формат.

Ето как можем да направим това:

assertThat(new DecimalFormat("#,###.##", new DecimalFormatSymbols(Locale.ENGLISH)).format(d)) .isEqualTo("1,234,567.89"); assertThat(new DecimalFormat("#,###.##", new DecimalFormatSymbols(Locale.ITALIAN)).format(d)) .isEqualTo("1.234.567,89"); 

Ако локалът, който ни интересува, не е сред тези, обхванати от конструктора DecimalFormatSymbols , можем да го посочим с метода getInstance :

Locale customLocale = new Locale("it", "IT"); assertThat(new DecimalFormat( "#,###.##", DecimalFormatSymbols.getInstance(customLocale)).format(d)) .isEqualTo("1.234.567,89");

5. Научни нотации

The Scientific Notation represents the product of a Mantissa and an exponent of ten. The number 1234567.89 can also be represented as 12.3456789 * 10^5 (the dot is shifted by 5 positions).

5.1. E-Notation

It's possible to express a number in Scientific Notation using the E pattern character representing the exponent of ten:

assertThat(new DecimalFormat("00.#######E0").format(d)) .isEqualTo("12.3456789E5"); assertThat(new DecimalFormat("000.000000E0").format(d)) .isEqualTo("123.456789E4"); 

We should keep in mind that the number of characters after the exponent is relevant, so if we need to express 10^12, we need E00 and not E0.

5.2. Engineering Notation

It's common to use a particular form of Scientific Notation called Engineering Notation, which adjusts results in order to be expressed as multiple of three, for example when using measuring units like Kilo (10^3), Mega (10^6), Giga (10^9), and so on.

We can enforce this kind of notation by adjusting the maximum number of integer digits (the characters expressed with the # and on the left of the decimal separator) so that it's higher than the minimum number (the one expressed with the 0) and higher than 1.

This forces the exponent to be a multiple of the maximum number, so for this use-case we want the maximum number to be three:

assertThat(new DecimalFormat("##0.######E0") .format(d)).isEqualTo("1.23456789E6"); assertThat(new DecimalFormat("###.000000E0") .format(d)).isEqualTo("1.23456789E6"); 

6. Parsing

Let's see how is possible to parse a String into a Number with the parse method:

assertThat(new DecimalFormat("", new DecimalFormatSymbols(Locale.ENGLISH)) .parse("1234567.89")) .isEqualTo(1234567.89); assertThat(new DecimalFormat("", new DecimalFormatSymbols(Locale.ITALIAN)) .parse("1.234.567,89")) .isEqualTo(1234567.89);

Тъй като върнатата стойност не се извежда от наличието на десетичен разделител, можем да използваме методите като .doubleValue () , .longValue () на върнатия обект Number, за да приложим конкретен примитив в изхода.

Също така можем да получим BigDecimal, както следва:

NumberFormat nf = new DecimalFormat( "", new DecimalFormatSymbols(Locale.ENGLISH)); ((DecimalFormat) nf).setParseBigDecimal(true); assertThat(nf.parse("1234567.89")) .isEqualTo(BigDecimal.valueOf(1234567.89)); 

7. Безопасност на резбата

DecimalFormat не е безопасен за нишки , поради което трябва да обърнем специално внимание, когато споделяме един и същ екземпляр между нишките.

8. Заключение

Видяхме основните употреби на класа DecimalFormat , заедно със силните и слабите му страни .

Както винаги, пълният изходен код е достъпен в Github.