5.11 Ausgaben formatieren
Immer wieder müssen Zahlen, Datumsangaben und Text auf verschiedenste Art und Weise formatiert werden. Zur Formatierung bietet Java mehrere Lösungen:
Die format(…)- und printf(…)-Methoden erlauben eine formatierte Ausgabe, so wie sie schon seit Urzeiten unter C mit printf(…) gesetzt wurde.
Formatieren über Format-Klassen: Allgemeines Formatierungsverhalten wird in einer abstrakten Klasse Format fixiert; konkrete Unterklassen, wie NumberFormat und DateFormat, nehmen sich spezielle Datenformate vor.
Umsetzung eines Strings nach einer gegebenen Maske mit einem MaskFormatter
Die Format-Klassen bieten nicht nur landes- und sprachabhängige Ausgaben per format(…), sondern auch den umgekehrten Weg, Zeichenketten wieder in Typen wie double oder Date zu zerlegen. Jede Zeichenkette, die vom Format-Objekt erzeugt wurde, kann auch mit dem Parser wieder eingelesen werden.
5.11.1 Formatieren und Ausgeben mit format()
Die Klasse String stellt mit der statischen Methode format(…) eine Möglichkeit bereit, um Zeichenketten nach einer Vorgabe zu formatieren.
[zB] Beispiel
String s = String.format( "Hallo %s. Es gab einen Anruf von %s.", "Chris", "Joy" );
System.out.println( s ); // Hallo Chris. Es gab einen Anruf von Joy.
Der erste an format(…) übergebene String nennt sich Format-String. Er enthält neben auszugebenden Zeichen weitere sogenannte Formatspezifizierer, die dem Formatierer darüber Auskunft geben, wie er das Argument formatieren soll. %s steht für eine unformatierte Ausgabe eines Strings. Nach dem Format-String folgt ein Vararg (oder alternativ das Array direkt) mit den Werten, auf die sich die Formatspezifizierer beziehen.
Spezifizierer | Steht für … | Spezifizierer | Steht für … |
---|---|---|---|
neue Zeile | Boolean | ||
Prozentzeichen | String | ||
Unicode-Zeichen | Dezimalzahl | ||
Hexadezimalschreibweise | Datum und Zeit | ||
Fließkommazahl | wissenschaftliche Notation |
[+] Tipp
Der Zeilenvorschub ist vom Betriebssystem abhängig, und %n gibt uns ein gutes Mittel an die Hand, an dieses Zeilenvorschubzeichen (oder diese Zeichenfolge) zu kommen. Aber statt mit String.format("%n") den Separator zu erfragen, ist System.lineSeparator() eine bessere Lösung.
final class java.lang.String
implements CharSequence, Comparable<String>, Serializable
static String format(String format, Object... args)
Liefert einen formatierten String, der aus dem String und den Argumenten hervorgeht.static String format(Locale l, String format, Object... args)
Liefert einen formatierten String, der aus der gewünschten Sprache, dem String und den Argumenten hervorgeht.
Intern werkeln java.util.Formatter (keine java.text.Format-Objekte), die sich auch direkt verwenden lassen; dort ist auch die Dokumentation festgemacht.
System.out.printf(…)
Soll eine mit String.format(…) formatierte Zeichenkette gleich ausgegeben werden, so muss dazu nicht System.out.print(String.format(format, args)); angewendet werden. Praktischerweise findet sich zum Formatieren und Ausgeben die aus String bekannte Methode format(…) auch in den Klassen PrintWriter und PrintStream (das System.out-Objekt ist vom Typ PrintStream). Da jedoch der Methodenname format(…) nicht wirklich konsistent mit den anderen printXXX(…)-Methoden ist, haben die Entwickler die format(…)-Methoden auch unter dem Namen printf(…) zugänglich gemacht. (Die Implementierung von printf(…) ist eine einfache Weiterleitung zur Methode format(…).)
[zB] Beispiel
Gib die Zahlen von 0 bis 15 hexadezimal aus:
for ( int i = 0x0; i <= 0xf; i++ )
System.out.printf( "%x%n", i ); // 0 1 2 ... e f
Auch bei printf(…) ist als erstes Argument ein Locale möglich.
Pimp my String mit Formatspezifizierern *
Die Anzahl der Formatspezifizierer ist so groß und ihre weitere Parametrisierung ist so vielfältig, dass ein Blick in die API-Dokumentation auf jeden Fall nötig ist. Die wichtigsten Spezifizierer sind:
%n ergibt das oder die Zeichen für den Zeilenvorschub, jeweils bezogen auf die aktuelle Plattform. Die Schreibweise ist einem harten \n vorzuziehen, da dies nicht das Zeilenvorschubzeichen der Plattform sein muss.
%% liefert das Prozentzeichen selbst, wie auch \\ in einem String den Backslash ausmaskiert.
%s liefert einen String, wobei null zur Ausgabe »null« führt. %S schreibt die Ausgabe groß.
%b schreibt ein Boolean, und zwar den Wert true oder false. Die Ausgabe ist immer false bei null und true bei anderen Typen wie Integer, String. %B schreibt den String groß.
%c schreibt ein Zeichen, wobei die Typen Character, Byte und Short erlaubt sind. %C schreibt das Zeichen in Großbuchstaben.
Für die ganzzahligen numerischen Ausgaben mit %d (dezimal), %x (hexadezimal), %o (oktal) sind Byte, Short, Integer, Long und BigInteger erlaubt – %X schreibt die hexadezimalen Buchstaben groß.
Bei den Fließkommazahlen mit %f oder %e (%E), %g (%G), %a (%A) sind zusätzlich die Typen Float, Double und BigDecimal zulässig. Die Standardpräzision für %e, %E, %f sind sechs Nachkommastellen.
Im Fall von Datums-/Zeitangaben mit %t bzw. %T sind erlaubt: Long, Calendar und Date. %t benötigt zwingend ein Suffix.
Den Hashcode schreibt %h bzw. %H. Beim Wert null ist auch das Ergebnis die Zeichenkette mit dem Inhalt »null«.
Zusätzliche Flags, etwa für Längenangaben und die Anzahl an Nachkommastellen, sind möglich und werden im folgenden Beispiel gezeigt:
PrintStream o = System.out;
int i = 123;
o.printf( "|%d|%d|%n" , i, -i ); // |123|-123|
o.printf( "|%5d|%5d|%n" , i, -i ); // | 123| -123|
o.printf( "|%-5d|%-5d|%n" , i, -i ); // |123 |-123 |
o.printf( "|%+-5d|%+-5d|%n" , i, -i ); // |+123 |-123 |
o.printf( "|%05d|%05d|%n%n", i, -i ); // |00123|-0123|
o.printf( "|%X|%x|%n", 0xabc, 0xabc ); // |ABC|abc|
o.printf( "|%04x|%#x|%n%n", 0xabc, 0xabc ); // |0abc|0xabc|
double d = 12345.678;
o.printf( "|%f|%f|%n" , d, -d ); // |12345,678000|-12345,678000|
o.printf( "|%+f|%+f|%n" , d, -d ); // |+12345,678000|-12345,678000|
o.printf( "|% f|% f|%n" , d, -d ); // | 12345,678000|-12345,678000|
o.printf( "|%.2f|%.2f|%n" , d, -d ); // |12345,68|-12345,68|
o.printf( "|%,.2f|%,.2f|%n" , d, -d ); // |12.345,68|-12.345,68|
o.printf( "|%.2f|%(.2f|%n", d, -d ); // |12345,68|(12345,68)|
o.printf( "|%10.2f|%10.2f|%n" , d, -d ); // | 12345,68| -12345,68|
o.printf( "|%010.2f|%010.2f|%n",d, -d ); // |0012345,68|-012345,68|
String s = "Monsterbacke";
o.printf( "%n|%s|%n", s ); // |Monsterbacke|
o.printf( "|%S|%n", s ); // |MONSTERBACKE|
o.printf( "|%20s|%n", s ); // | Monsterbacke|
o.printf( "|%-20s|%n", s ); // |Monsterbacke |
o.printf( "|%7s|%n", s ); // |Monsterbacke|
o.printf( "|%.7s|%n", s ); // |Monster|
o.printf( "|%20.7s|%n", s ); // | Monster|
Date t = new Date();
o.printf( "%tT%n", t ); // 11:01:39
o.printf( "%tD%n", t ); // 12/12/12
o.printf( "%1$te. %1$tb%n", t ); // 12. Dez
Im Fall von Fließkommazahlen werden diese nach dem RoundingMode.HALF_UP gerundet, sodass etwa System.out.printf("%.1f", 0.45); die Ausgabe »0,5« ergibt.
Aus den Beispielen lassen sich einige Flags ablesen, insbesondere bei Fließkommazahlen. Ein Komma steuert, ob Tausendertrenner eingesetzt werden. Ein + gibt an, ob immer ein Vorzeichen erscheint; und ein Leerzeichen besagt, ob dann bei positiven Zeichen ein Platz frei bleibt. Eine öffnende Klammer setzt bei negativen Zahlen kein Minus, sondern setzt diese in Klammern.
[zB] Beispiel
Gib die Zahlen von 1 bis 10 aus. Die Zahlen 1 bis 9 sollen eine führende Null bekommen.
for ( int i = 1 ; i < 11; i++ )
System.out.printf( "%02d%n", i ); // 01 02 ... 10
Formatspezifizierer für Datumswerte
Aus dem größeren vorangehenden Beispiel wird ersichtlich, dass %t nicht einfach die Zeit ausgibt, sondern immer ein weiteres Suffix erwartet, das genau angibt, welcher Datums-/Zeitteil eigentlich gewünscht ist. Tabelle 5.13 gibt die wichtigsten Suffixe an, und weitere finden Sie in der API-Dokumentation. Alle Ausgaben berücksichtigen die gegebene Locale-Umgebung.
Beschreibung | |
---|---|
%tA, %ta | vollständiger/abgekürzter Name des Wochentags |
%tB, %tb | vollständiger/abgekürzter Name des Monatsnamens |
%tC | zweistelliges Jahrhundert (00–99) |
%te, %td | Monatstag numerisch ohne bzw. mit führenden Nullen (1–31 bzw. 01–31) |
%tk, %tl | Stundenangabe bezogen auf 24 bzw. 12 Stunden (0–23, 1–12) |
%tH, %tI | zweistellige Stundenangabe bezogen auf 24 bzw. 12 Stunden (00–23, 01–12) |
%tj | Tag des Jahres (001–366) |
%tM | zweistellige Minutenangabe (00–59) |
%tm | zweistellige Monatsangabe (in der Regel 01–12) |
%tS | zweistellige Sekundenangabe (00–59) |
%tY | vierstellige Jahresangabe |
%ty | die letzten beiden Ziffern der Jahresangabe (00–99) |
%tZ | abgekürzte Zeitzone |
%tz | Zeitzone mit Verschiebung zur GMT |
%tR | Stunden und Minuten in der Form %tH:%tM |
%tT | Stunden/Minuten/Sekunden in der Form %tH:%tM:%tS |
%tD | Datum in der Form %tm/%td/%ty |
%tF | ISO-8601-Format %tY-%tm-%td |
%tc | komplettes Datum mit Zeit in der Form %ta %tb %td %tT %tZ %tY |
Positionsangaben
Im vorangegangenen Beispiel PrintfDemo lautete eine Zeile sinngemäß:
System.out.printf( "%te. %1$tb%n", t ); // 28. Okt
Der Formatierungsstring enthält eine Positionsangabe Position$, bei der sich 1$ auf das erste Argument, 2$ auf das zweite usw. bezieht. (Interessant ist, dass hier die Nummerierung nicht bei null beginnt.)
Die Positionsangabe im Format-String ermöglicht zwei Dinge:
Wird, wie in dem Beispiel, das gleiche Argument mehrmals verwendet, ist es unnötig, es mehrmals anzugeben. So wiederholt printf("%te. %tb%n", t, t) das Argument t, was die Angabe einer Position vermeidet. Statt dieser Lösung lässt sich %1$te. %1$tb%n schreiben, also auch für das erste Argument ausdrücklich die Position 1 vorschreiben.
Die Reihenfolge der übergebenen Argumente bleibt in der Regel immer gleich, aber der Format-String kann die Argumente an unterschiedliche Positionen setzen.
Der zweite Punkt ist wichtig für lokalisierte Ausgaben. Dazu ein Beispiel: Eine Bildschirmausgabe soll den Vor- und Nachnamen in unterschiedlichen Sprachen ausgeben. Die Reihenfolge der Namensbestandteile kann jedoch unterschiedlich sein, und nicht immer steht in jeder Sprache der Vorname vor dem Nachnamen. Im Deutschen heißt es im Willkommenstext dann »Hallo Christian Ullenboom«, aber in der (erfundenen) Sprache Bwatuti hieße es »Jambo Ullenboom Christian«:
Object[] formatArgs = { "Christian", "Ullenboom" };
String germanFormat = "Hallo %1$s %2$s";
System.out.printf( germanFormat, formatArgs );
System.out.println();
String bwatutiFormat = "Jambo %2$s %1$s";
System.out.printf( bwatutiFormat, formatArgs );
Die Aufrufreihenfolge für Vor-/Nachname ist immer die gleiche, aber der Format-String, der zum Beispiel extern aus einer Konfigurationsdatei oder Datenbank kommt, kann diese Reihenfolge ändern und so der Landessprache anpassen.
[+] Tipp
Bezieht sich ein nachfolgendes Formatelement auf das vorangehende Argument, so kann ein < gesetzt werden:
LocalDate c1 = LocalDate.of( 1973, Month.MARCH, 1 );
LocalDate c2 = LocalDate.of( 1985, 9, 2 );
System.out.printf( "%te. %<tb %<ty, %2$te. %<tb %<ty%n",
c1, c2 ); // 1. März 73, 2. Sep. 85
Die Angaben für Monat und Jahr beziehen sich jeweils auf die vorangehenden Positionen. So muss nur einmal c1 und c2 angegeben werden.