Ein praktischer Leitfaden für DecimalFormat

1. Übersicht

In diesem Artikel werden wir die DecimalFormat- Klasse zusammen mit ihren praktischen Verwendungen untersuchen.

Dies ist eine Unterklasse von Numberformat , die Zahlen dezimaler Formatierung ermöglicht String unter Verwendung von vordefinierten Mustern abweichen.

Es kann auch umgekehrt verwendet werden, um Strings in Zahlen zu analysieren.

2. Wie funktioniert es?

Um eine Zahl zu formatieren, müssen wir ein Muster definieren, bei dem es sich um eine Folge von Sonderzeichen handelt, die möglicherweise mit Text gemischt sind.

Es gibt 11 spezielle Musterzeichen, aber die wichtigsten sind:

  • 0 - druckt eine Ziffer, falls angegeben, andernfalls 0
  • # - druckt eine Ziffer, falls angegeben, sonst nichts
  • . - Geben Sie an, wo das Dezimaltrennzeichen platziert werden soll
  • , - Geben Sie an, wo das Gruppentrennzeichen platziert werden soll

Wenn das Muster auf eine Reihe angewendet wird, dessen Formatierungsregeln ausgeführt werden , und das Ergebnis gedruckt wird entsprechend der DecimalFormatSymbol unserer JVM Locale es sei denn , eine bestimmte Locale angegeben wird.

Die Ausgaben der folgenden Beispiele stammen von einer JVM, die in einem englischen Gebietsschema ausgeführt wird .

3. Grundlegende Formatierung

Lassen Sie uns nun sehen, welche Ausgaben beim Formatieren derselben Nummer mit den folgenden Mustern erzeugt werden.

3.1. Einfache Dezimalstellen

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

Wie wir sehen können, wird der ganzzahlige Teil niemals verworfen, egal ob das Muster kleiner als die Zahl ist.

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

Wenn das Muster stattdessen größer als die Zahl ist, werden Nullen hinzugefügt, während Hashes sowohl im Ganzzahl- als auch im Dezimalteil gelöscht werden.

3.2. Rundung

Wenn der Dezimalteil des Musters nicht die gesamte Genauigkeit der eingegebenen Zahl enthalten kann, wird er gerundet.

Hier wurde der .89-Teil auf .90 gerundet, dann wurde die 0 gelöscht:

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

Hier wurde der .89-Teil auf 1,00 gerundet, dann wurde der .00-Wert gelöscht und die 1 auf die 7 summiert:

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

Der Standardrundungsmodus ist HALF_EVEN, kann jedoch über die setRoundingMode- Methode angepasst werden.

3.3. Gruppierung

Das Gruppentrennzeichen wird verwendet, um ein Untermuster anzugeben, das automatisch wiederholt wird:

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

3.4. Mehrere Gruppierungsmuster

Einige Länder haben eine variable Anzahl von Gruppierungsmustern in ihren Nummerierungssystemen.

Das indische Nummerierungssystem verwendet das Format #, ##, ###. ##, in dem nur das erste Gruppentrennzeichen drei Zahlen enthält, während alle anderen zwei Zahlen enthalten.

Dies ist mit der DecimalFormat- Klasse nicht möglich. Sie behält nur das neueste Muster bei, das von links nach rechts angetroffen wird, und wendet es auf die gesamte Zahl an, wobei vorherige Gruppierungsmuster ignoriert werden.

Ein Versuch, das Muster #, ##, ##, ##, ### zu verwenden, würde zu einer Umgruppierung zu #######, ### führen und zu einer Umverteilung zu #, ###, # führen. ##, ###.

Um eine Übereinstimmung mit mehreren Gruppierungsmustern zu erreichen, müssen Sie unseren eigenen String- Manipulationscode schreiben oder alternativ das DecimalFormat des Icu4J ausprobieren , das dies ermöglicht.

3.5. String-Literale mischen

Es ist möglich, String- Literale innerhalb des Musters zu mischen :

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

Es ist auch möglich, Sonderzeichen als String- Literale zu verwenden, indem Sie Folgendes maskieren:

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

4. Lokalisierte Formatierung

Viele Länder verwenden keine englischen Symbole und verwenden das Komma als Dezimaltrennzeichen und den Punkt als Gruppentrennzeichen.

Das Ausführen des Musters #, ###. ## auf einer JVM mit einem italienischen Gebietsschema würde beispielsweise 1.234.567,89 ausgeben.

Während dies in einigen Fällen eine nützliche i18n-Funktion sein kann, möchten wir in anderen Fällen möglicherweise ein bestimmtes, JVM-unabhängiges Format erzwingen.

So können wir das machen:

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"); 

Wenn das Gebietsschema, an dem wir interessiert sind, nicht zu den vom DecimalFormatSymbols- Konstruktor abgedeckten gehört , können wir es mit der Methode getInstance angeben :

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

5. Wissenschaftliche Notationen

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);

Da der zurückgegebene Wert nicht durch das Vorhandensein eines Dezimaltrennzeichens abgeleitet wird, können wir Methoden wie .doubleValue () , .longValue () des zurückgegebenen Number- Objekts verwenden, um ein bestimmtes Grundelement in der Ausgabe zu erzwingen.

Wir können ein BigDecimal auch wie folgt erhalten:

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

7. Gewindesicherheit

DecimalFormat ist nicht threadsicher , daher sollten wir besonders darauf achten, wenn wir dieselbe Instanz zwischen Threads teilen.

8. Fazit

Wir haben die wichtigsten Verwendungszwecke der DecimalFormat- Klasse sowie ihre Stärken und Schwächen gesehen .

Wie immer ist der vollständige Quellcode auf Github verfügbar.