Rheinwerk Computing < openbook > Rheinwerk Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger.

Inhaltsverzeichnis
Vorwort
1 Java ist auch eine Sprache
2 Imperative Sprachkonzepte
3 Klassen und Objekte
4 Der Umgang mit Zeichenketten
5 Eigene Klassen schreiben
6 Exceptions
7 Äußere.innere Klassen
8 Besondere Klassen der Java SE
9 Generics<T>
10 Architektur, Design und angewandte Objektorientierung
11 Die Klassenbibliothek
12 Einführung in die nebenläufige Programmierung
13 Einführung in Datenstrukturen und Algorithmen
14 Einführung in grafische Oberflächen
15 Einführung in Dateien und Datenströme
16 Einführung in die <XML>-Verarbeitung mit Java
17 Einführung ins Datenbankmanagement mit JDBC
18 Bits und Bytes und Mathematisches
19 Die Werkzeuge des JDK
A Die Klassenbibliothek
Stichwort

Download:
- Aufgaben, ca. 1,1 MB
- Programme, ca. 12,8 MB

Buch bestellen
Ihre Meinung?

Spacer
Java ist auch eine Insel von Christian Ullenboom
Das umfassende Handbuch
Buch: Java ist auch eine Insel

Java ist auch eine Insel
Galileo Computing
1308 S., 10., aktualisierte Auflage, geb., mit DVD
ca. 49,90 Euro, ISBN 978-3-8362-1802-3
Pfeil 4 Der Umgang mit Zeichenketten
Pfeil 4.1 Von ASCII über ISO-8859-1 zu Unicode
Pfeil 4.1.1 ASCII
Pfeil 4.1.2 ISO/IEC 8859-1
Pfeil 4.1.3 Unicode
Pfeil 4.1.4 Unicode-Zeichenkodierung
Pfeil 4.1.5 Escape-Sequenzen/Fluchtsymbole
Pfeil 4.1.6 Schreibweise für Unicode-Zeichen und Unicode-Escapes
Pfeil 4.1.7 Unicode 4.0 und Java *
Pfeil 4.2 Die Character-Klasse
Pfeil 4.2.1 Ist das so?
Pfeil 4.2.2 Zeichen in Großbuchstaben/Kleinbuchstaben konvertieren
Pfeil 4.2.3 Ziffern einer Basis *
Pfeil 4.3 Zeichenfolgen
Pfeil 4.4 Die Klasse String und ihre Methoden
Pfeil 4.4.1 String-Literale als String-Objekte für konstante Zeichenketten
Pfeil 4.4.2 Konkatenation mit +
Pfeil 4.4.3 String-Länge und Test auf Leerstring
Pfeil 4.4.4 Zugriff auf ein bestimmtes Zeichen mit charAt( )
Pfeil 4.4.5 Nach enthaltenen Zeichen und Zeichenfolgen suchen
Pfeil 4.4.6 Das Hangman-Spiel
Pfeil 4.4.7 Gut, dass wir verglichen haben
Pfeil 4.4.8 Phonetische Vergleiche *
Pfeil 4.4.9 String-Teile extrahieren
Pfeil 4.4.10 Strings anhängen, Groß-/Kleinschreibung und Leerraum
Pfeil 4.4.11 Suchen und ersetzen
Pfeil 4.4.12 String-Objekte mit Konstruktoren neu anlegen *
Pfeil 4.5 Konvertieren zwischen Primitiven und Strings
Pfeil 4.5.1 Unterschiedliche Typen in String-Repräsentationen konvertieren
Pfeil 4.5.2 Stringinhalt in einen primitiven Wert konvertieren
Pfeil 4.5.3 String-Repräsentation im Format Binär, Hex, Oktal *
Pfeil 4.6 Veränderbare Zeichenketten mit StringBuilder und StringBuffer
Pfeil 4.6.1 Anlegen von StringBuilder/StringBuffer-Objekten
Pfeil 4.6.2 StringBuilder/StringBuffer in andere Zeichenkettenformate konvertieren
Pfeil 4.6.3 Zeichen(folgen) erfragen
Pfeil 4.6.4 Daten anhängen
Pfeil 4.6.5 Zeichen(folgen) setzen, löschen und umdrehen
Pfeil 4.6.6 Länge und Kapazität eines StringBuilder/StringBuffer-Objekts *
Pfeil 4.6.7 Vergleichen von String mit StringBuilder und StringBuffer
Pfeil 4.6.8 hashCode() bei StringBuilder/StringBuffer *
Pfeil 4.7 CharSequence als Basistyp *
Pfeil 4.8 Reguläre Ausdrücke
Pfeil 4.8.1 Pattern.matches() bzw. String#matches()
Pfeil 4.8.2 Die Klassen Pattern und Matcher
Pfeil 4.8.3 Finden und nicht matchen
Pfeil 4.8.4 Gierige und nicht gierige Operatoren *
Pfeil 4.8.5 Mit MatchResult alle Ergebnisse einsammeln *
Pfeil 4.8.6 Suchen und Ersetzen mit Mustern
Pfeil 4.8.7 Hangman Version 2
Pfeil 4.9 Zerlegen von Zeichenketten
Pfeil 4.9.1 Splitten von Zeichenketten mit split()
Pfeil 4.9.2 Die Klasse Scanner
Pfeil 4.9.3 Die Klasse StringTokenizer *
Pfeil 4.9.4 BreakIterator als Zeichen-, Wort-, Zeilen- und Satztrenner *
Pfeil 4.10 Zeichenkodierungen, XML/HTML-Entitys, Base64 *
Pfeil 4.10.1 Unicode und 8-Bit-Abbildungen
Pfeil 4.10.2 Das Paket java.nio.charset und der Typ Charset
Pfeil 4.10.3 Konvertieren mit OutputStreamWriter/InputStreamReader-Klassen *
Pfeil 4.10.4 XML/HTML-Entitys ausmaskieren
Pfeil 4.10.5 Base64-Kodierung
Pfeil 4.11 Ausgaben formatieren
Pfeil 4.11.1 Formatieren und Ausgeben mit format()
Pfeil 4.11.2 Die Formatter-Klasse *
Pfeil 4.11.3 Formatieren mit Masken *
Pfeil 4.11.4 Format-Klassen
Pfeil 4.11.5 Zahlen, Prozente und Währungen mit NumberFormat und DecimalFormat formatieren *
Pfeil 4.11.6 MessageFormat und Pluralbildung mit ChoiceFormat
Pfeil 4.12 Sprachabhängiges Vergleichen und Normalisierung *
Pfeil 4.12.1 Die Klasse Collator
Pfeil 4.12.2 Effiziente interne Speicherung für die Sortierung
Pfeil 4.12.3 Normalisierung
Pfeil 4.13 Zum Weiterlesen

Rheinwerk Computing - Zum Seitenanfang

4.11 Ausgaben formatierenZur nächsten Überschrift

Immer wieder müssen Zahlen, Datumsangaben und Text auf verschiedenste Art und Weise formatiert werden. Zur Formatierung bietet Java mehrere Lösungen:

  • Seit Java 5 realisieren die format()- und printf()-Methoden eine Ausgabe, so wie sie 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- beziehungsweise 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.

Rheinwerk Computing - Zum Seitenanfang

4.11.1 Formatieren und Ausgeben mit format()Zur nächsten ÜberschriftZur vorigen Überschrift

Die Klasse String stellt mit der statischen Methode format() eine Möglichkeit bereit, Zeichenketten nach einer Vorgabe zu formatieren.

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 Format-Spezifizierer, 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 Feld direkt) mit den Werten, auf die sich die Format-Spezifizierer beziehen.

Tabelle 4.20: Die wichtigsten Format-Spezifizierer im Überblick

Spezifizierer Steht für... Spezifizierer Steht für...
%n neue Zeile %b Boolean
%% Prozentzeichen %s String
%c Unicode-Zeichen %d Dezimalzahl
%x Hexadezimalschreibweise %t Datum und Zeit
%f Fließkommazahl %e wissenschaftliche Notation
Tipp

Der Zeilenvorschub ist vom Betriebssystem abhängig, und %n gibt uns ein gutes Mittel an die Hand, um an dieses Zeilenvorschubzeichen (oder diese Zeichenfolge) zu kommen. Dann kommt der format()-Aufruf auch mit einem Argument aus, und es lautet String.format("%n").

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 zu 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()).

Beispiel

Gib die Zahlen von 0 bis 16 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 Format-Spezifizierern *

Die Anzahl der Format-Spezifizierer 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 beziehungsweise 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 beziehungsweise %T sind erlaubt: Long, Calendar und Date. %t benötigt zwingend ein Suffix.
  • Den Hashcode schreibt %h beziehungsweise %H. Beim Wert null ist auch das Ergebnis »null«.

Zusätzliche Flags, etwa für Längenangaben und die Anzahl an Nachkommastellen, sind möglich und werden im folgenden Beispiel gezeigt:

Listing 4.26: PrintfDemo.java, main()

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 ); // 04/18/08
o.printf( "%1$te. %1$tb%n", t ); // 18. Apr

Im Fall von Fließkommazahlen werden diese nach dem Modus BigDecimal.ROUND_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 angegeben wird, und ein Leerzeichen besagt, ob dann bei positiven Zeichen ein Platz freibleibt. Eine öffnende Klammer setzt bei negativen Zahlen kein Minus, sondern setzt diese in Klammern.

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

Format-Spezifizierer für Datumswerte

Aus dem 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 4.21 gibt die wichtigsten Suffixe an, und weitere finden Sie in der API-Dokumentation. Alle Ausgaben berücksichtigen die gegebene Locale-Umgebung.

Tabelle 4.21: Suffixe für Datumswerte

Symbol 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 lautete eine Zeile:

System.out.printf( "%te. %1$tb%n", t );     // 28. Okt

Die Angabe mit Position$ ist eine Positionsangabe, und so bezieht sich 1$ auf das erste Argument, 2$ auf das zweite und so weiter (interessant ist, dass hier die Nummerierung nicht bei null beginnt).

Die Positionsangabe im Formatstring 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 %te. %1$tb%n lässt sich natürlich auch %1$te. %1$tb%n schreiben, also auch für das erste Argument ausdrücklich die Position 1 vorschreiben.
  • Die Reihenfolge der Parameter kann immer gleich bleiben, aber der Formatstring kann die Reihenfolge später ändern.

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«:

Listing 4.27: FormatPosition.java, main()

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 Formatstring, 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:

Calendar c1 = new GregorianCalendar( 1973, 2, 12 );
Calendar c2 = new GregorianCalendar( 1985, 8, 2 );
System.out.printf( "%te. %<tb %<ty, %2$te. %<tb %<ty%n",
c1, c2 ); // 12. Mrz 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.


Rheinwerk Computing - Zum Seitenanfang

4.11.2 Die Formatter-Klasse *Zur nächsten ÜberschriftZur vorigen Überschrift

Die Methoden format() und prinf() übernehmen die Aufbereitung nicht selbst, sondern delegieren sie an die Klasse java.util.Formatter. Das ist auch der Grund, warum die Dokumentation für die Formatspezifizierer nicht etwa an String.format() hängt, sondern an Formatter.

Ein Blick auf die Methode format() der Klasse String verrät, wie der Formatter ins Spiel kommt:

Listing 4.28: java.lang.String, format()

public static String format( String format, Object ... args )
{
return new Formatter().format( format, args ).toString();
}

Ein Formatter übernimmt zwei Aufgaben. Er übernimmt zum einen die tatsächliche Formatierung, und zum anderen gibt er die formatierten Ausgaben an ein Ziel weiter. Wird der Formatter mit dem Standardkonstruktor aufgerufen, so baut er selbst das Ausgabeziel aus einem StringBuilder auf, den folgende format()-Aufrufe dann füllen. toString() vom Formatter ist so implementiert, dass es auf dem Ausgabeziel (also in unserem Fall dem StringBuilder) toString() aufruft.

Das Wissen um diesen Mechanismus ist für die Optimierung wichtig, um nicht zu viele Zwischenobjekte zu erzeugen. So führt die Schleife

StringBuilder sb = new StringBuilder();
for ( double d = 0; d <= 1; d += 0.1 )
{
String s = String.format( "%.1f%n", d );
sb.append( s );
}
System.out.println( sb ); // 0,1 0,2 ... 1,0

zu:

StringBuilder sb = new StringBuilder();
for ( double d = 0; d <= 1; d += 0.1 )
{
String s = new Formatter().format( "%.1f%n", d ).toString();
sb.append( s );
}
System.out.println( sb ); // 0,1 0,2 ... 1,0

Bei jedem Schleifendurchlauf wird also ein neuer Formatter aufgebaut. Intern entsteht damit ein neuer StringBuilder als Ziel für die formatierten Strings und schlussendlich über toString() ein String-Objekt. Nicht zu vergessen sind die internen char-Felder und der GC, der die Objekte wieder wegräumen muss.

Würden wir gleich das Ziel angeben, so könnte das viel effizienter werden. Dazu wird nicht der Standardkonstruktor von Formatter eingesetzt, der das Ziel mit einem neuen StringBuilder vorbestimmt, sondern ein eigenes Zielobjekt, das unser StringBuilder sein kann (es ist alles erlaubt, was vom Typ Appendable ist). Optimiert folgt somit:

Listing 4.29: FormatterDemo.java, main()

StringBuilder sb = new StringBuilder();
Formatter formatter = new Formatter( sb );

for ( double d = 0; d <= 1; d += 0.1 )
formatter.format( "%.1f%n", d );

System.out.println( formatter ); // 0,1 0,2 ... 1,0

Wir weisen in der Schleife den Formatter an, die Formatierung vorzunehmen. Da dieser mit dem Ziel StringBuilder aufgebaut wurde, füllen die Zahlen nach und nach unseren StringBuilder. Temporäre Zwischenobjekte werden so minimiert. Zum Schluss wird der Formatter nach dem Ergebnis gefragt.

Formattable und formatTo()

Der Formatspezifizierer %s kann auf jedem Argumenttyp angewendet werden, denn durch die Varargs werden auch primitive Elemente zu Wrapper-Objekten (zu Wrapper-Klassen, siehe Abschnitt 8.2, »Wrapper-Klassen und Autoboxing«), die eine toString()-Methode haben. Nun kann es aber sein, dass toString() besonders implementiert werden muss und nicht unbedingt die Zeichenkette liefert, die für die Ausgabe gewünscht ist. Für diesen Fall berücksichtigt der Formatter einen besonderen Typ. Implementiert die Klasse die besondere Schnittstelle java.util.Formattable, so ruft der Formatter nicht die toString()-Methode auf, sondern formatTo(Formatter formatter, int flags, int width, int precision). Die API-Dokumentation liefert ein Beispiel.


Rheinwerk Computing - Zum Seitenanfang

4.11.3 Formatieren mit Masken *Zur nächsten ÜberschriftZur vorigen Überschrift

Oftmals unterscheidet sich bei grafischen Oberflächen die Darstellung von Daten von dem tieferliegenden Datenmodell. Während ein Datum zum Beispiel intern als große Zahl vorliegt, soll der Anwender sie in der gewünschten Landessprache sehen können. Bei einigen Ausgaben kommen Trennzeichen in die Ausgabe, um sie für den Leser besser verständlich zu machen. Eine IP-Adresse enthält Punkte an ganz bestimmen Stellen, eine Telefonnummer trennt die Vorwahl vom Rest ab, und die Segmente eines Datums trennen in der Regel die Zeichen »/« oder »-«.

Für Formatierungen, bei denen ein Originalstring in einen Ausgabestring konvertiert wird und dabei neue Zeichen zur Ausgabe eingefügt werden, bietet die Java-API eine Klasse javax.swing.text.MaskFormatter. Die Swing-Klasse hilft bei der Formatierung und dem Parsen:

Listing 4.30: MaskFormatterDemo.java, main()

MaskFormatter mf = new MaskFormatter( "**-**-****" );
mf.setValueContainsLiteralCharacters( false );
String valueToString = mf.valueToString( "12031973" );
System.out.println( valueToString ); // 12-03-1973
Object stringToValue = mf.stringToValue( valueToString );
System.out.println( stringToValue ); // 12031973

Der Konstruktor von MaskFormatter bekommt ein Muster, wobei es Platzhalter gibt. Das Sternchen * steht für ein Zeichen. Die Methode valueToString() bringt einen String in das Muster. Der gemusterte String wandelt stringToValue() wieder in das Original um.

Das Schöne ist, dass die Muster-Definitionen aus einer externen Quelle stammen können, ohne den Programmcode mit speziellen Formatierungsanweisungen zu verschmutzen. Neben * gibt es weitere Platzhalter, die erlaubte Zeichen eingrenzen, sodass bei der Umwandlung mit valueToString() eine ParseException ausgelöst wird, wenn das Zeichen nicht im Format des Musterplatzhalters ist.

Tabelle 4.22: Musterplatzhalter

Musterzeichen Steht für
* jedes Zeichen
# eine Zahl, wie Character.isDigit()sie testet
? ein Zeichen nach Character.isLetter()
A ein Zeichen oder eine Ziffer, also Character.isLetter() oder Character.isDigit()
U ein Zeichen nach Character.isLetter(), aber konvertiert in einen Großbuchstaben
L ein Zeichen nach Character.isLetter(), aber konvertiert in einen Kleinbuchstaben
H ein Hexadezimalzeichen (0–9, a–f oder A–F)
' einen ausmaskierten und nicht interpretierten Bereich

Weitere Möglichkeiten der Klasse beschreibt die API-Dokumentation.


Rheinwerk Computing - Zum Seitenanfang

4.11.4 Format-KlassenZur nächsten ÜberschriftZur vorigen Überschrift

Die Methode format() formatiert Zahlen, Datumswerte und sonstige Ausgaben und benötigt wegen ihrer Komplexität eine Beschreibung von mehreren Bildschirmseiten. Dabei gibt es noch eine andere Möglichkeit, für unterschiedliche Typen von zu formatierenden Werten eigene Klassen zu haben:

  • DateFormat: Formatieren von Datums-/Zeitwerten
  • NumberFormat: Formatieren von Zahlen
  • MessageFormat: Formatieren für allgemeine Programmmeldungen

Die Klassen haben gemeinsam, dass sie die abstrakte Klasse Format erweitern und so eine gemeinsame Schnittstelle haben. Jede dieser Klassen implementiert auf jeden Fall die Methode format() zur Ausgabe und zum Parsen, also zur Konvertierung vom String in das Zielobjekt, die Methode parseObject().

Zwei Gründe sprechen für den Einsatz der Format-Klassen:

  • Es gibt in String zwar eine format()-Methode, aber keine parseXXX()-Methode.
  • Die Format-Klassen liefern mit statischen getXXXInstance()-Methoden vordefinierte Format-Objekte, die übliche Standardausgaben erledigen, etwa gerundete Ganzzahlen, Prozente oder unterschiedlich genaue Datums-/Zeitangaben.

Das folgende Beispiel zeigt einige Anwendungen zum zweiten Punkt.

Tabelle 4.23: Formatobjekte im Einsatz

Ergebnis Formatiert mit
02.09.2005 DateFormat.getDateInstance().format( new Date() )
15:25:16 DateFormat.getTimeInstance().format( new Date() )
02.09.2005 15:25:16 DateFormat.getDateTimeInstance().format( new Date() )
12.345,679 NumberFormat.getInstance().format( 12345.6789 )
12.345,68 € NumberFormat.getCurrencyInstance().format( 12345.6789 )
12 % NumberFormat.getPercentInstance().format( 0.123 )
Abbildung

Abbildung 4.11: DateFormat, MessageFormat und NumberFormat erweitern die abstrakte Klasse Format. Die Unterklassen übernehmen die Ein-/Ausgabe für Datumsangaben, für allgemeine Programmmeldungen und für Zahlen.

Beim Einsatz von DateFormat.getDateInstance().format(date) berücksichtigt die Methode korrekt je nach Land die Reihenfolge von Tag, Monat und Jahr und das Trennzeichen. Bei einem String.format() über %t müssten die drei Segmente je nach Sprache in die richtige Reihenfolge gebracht werden, sodass die Variante über DateFormat besser ist.

Hinweis

NumberFormat stellt die Zahlen nicht in Exponentialschreibweise dar, und standardmäßig ist die Anzahl an Nachkommastellen beschränkt:

System.out.println( NumberFormat.getInstance().format( 2E30 ) );
System.out.println( NumberFormat.getInstance().format( 2E-30 ) );

Die Ausgabe ist:

2.000.000.000.000.000.000.000.000.000.000
0

abstract class java.text.Format
implements Serializable, Cloneable
  • String format(Object obj)
    Formatiert das Objekt obj und gibt eine Zeichenkette zurück.
  • abstract StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos)
    Formatiert ein Objekt und hängt den Text an den angegebenen StringBuffer an (eine Methode mit StringBuilder gibt es nicht). Kann die Zeichenkette nicht mit format() nach den Regeln des Format-Objekts ausgegeben werden, löst die Methode eine IllegalArgumentException aus. Ist die Formatierungsanweisung falsch, so gibt format() das Unicode-Zeichen \uFFFD zurück.
  • Object parseObject(String source)
    Analysiert den Text von Anfang an.
  • abstract Object parseObject(String source, ParsePosition pos)
    Der Text wird ab der Stelle pos umgewandelt. Konnte parseObject() die Zeichenkette nicht zurückübersetzen, so folgt eine ParseException. parseObject(String, ParsePosition) verändert das ParsePosition-Objekt nicht und gibt die null-Referenz zurück.
  • Object clone()
    Gibt eine Kopie zurück.

Die Mehrzahl der Format-Unterklassen implementiert statische Fabrikmethoden der Art:

  • static XXXFormat getYYYInstance()
    Liefert ein Formatierungsobjekt mit den Formatierungsregeln für das voreingestellte Land.
  • static XXXFormat getYYYInstance(Locale l)
    Liefert ein Formatierungsobjekt mit den Formatierungsregeln für das angegebene Land. So erlauben die Unterklassen von Format es dem Benutzer auch, weitere Objekte zu erzeugen, die an die speziellen Sprachbesonderheiten der Länder angepasst sind.

Rheinwerk Computing - Zum Seitenanfang

4.11.5 Zahlen, Prozente und Währungen mit NumberFormat und DecimalFormat formatieren *Zur nächsten ÜberschriftZur vorigen Überschrift

NumberFormat widmet sich der Ausgabe von Zahlen. Dabei unterstützt die Klasse vier Typen von Ausgaben, für die es jeweils eine statische Fabrikmethode gibt.

abstract class java.text.NumberFormat
extends Format
  • static NumberFormat getNumberInstance()
    Liefert den einfachen Formatierer für Zahlen.
  • static NumberFormat getIntegerInstance()
    Liefert einen Formatierer, der den Nachkommateil abschneidet und rundet.
  • static NumberFormat getPercentInstance()
    Liefert einen Formatierer, der Fließkommazahlen über die format()-Methode im Bereich von 0 bis 1 annimmt und dann als Prozentzahl formatiert. Nachkommastellen werden abgeschnitten.
  • static NumberFormat getCurrencyInstance()
    Liefert einen Formatierer für Währungen, der ein Währungszeichen zur Ausgabe hinzufügt.

Die genannten vier statischen Methoden gibt es jeweils in der parameterlosen Variante und in der Variante mit einem Locale-Objekt, um etwa das Währungszeichen oder das Kommazeichen anzupassen.

Dezimalzahlformatierung mit DecimalFormat

DecimalFormat ist eine Unterklasse von NumberFormat und ermöglicht individuellere Anpassungen an die Ausgabe. Dem Konstruktor kann ein Formatierungsstring übergeben werden, sozusagen eine Vorlage, wie die Zahlen zu formatieren sind. Die Formatierung einer Zahl durch DecimalFormat erfolgt mit Rücksicht auf die aktuell eingestellte Sprache:

Listing 4.31: DecimalFormatDemo.java, main()

double d = 12345.67890;
DecimalFormat df = new DecimalFormat( "###,##0.00" );
System.out.println( df.format(d) ); // 12.345,68

Der Formatierungsstring kann eine Menge von Formatierungsanweisungen vertragen; im Beispiel kommen #, 0 und das Komma vor. Die beiden wichtigen Symbole sind jedoch 0 und #. Beide repräsentieren Ziffern. Der Unterschied tritt erst dann zutage, wenn weniger Zeichen zum Formatieren da sind, als im Formatierungsstring genannt werden.

Tabelle 4.24: Formatierungsanweisungen für »DecimalFormat«

Symbol Bedeutung
0 Repräsentiert eine Ziffer – ist die Stelle nicht belegt, wird eine Null angezeigt.
# Repräsentiert eine Ziffer – ist die Stelle nicht belegt, bleibt sie leer, damit führende Nullen und unnötige Nullen hinter dem Komma nicht angezeigt werden.
. Dezimaltrenner. Trennt Vor- und Nachkommastellen.
, Gruppiert die Ziffern (eine Gruppe ist so groß wie der Abstand von
»,« zu ».«).
; Trennzeichen. Links davon steht das Muster für positive Zahlen, rechts davon das Muster für negative Zahlen.
- Das Standardzeichen für das Negativpräfix
% Die Zahl wird mit 100 multipliziert und als Prozentwert ausgewiesen.
\u2030 Die Zahl wird mit 1.000 multipliziert und als Promillewert ausgezeichnet.
\u00A4 Nationales Währungssymbol (€ für Deutschland)
\u00A4\u00A4 Internationales Währungssymbol (EUR für Deutschland)
X Alle anderen Zeichen – symbolisch X – können ganz normal benutzt werden.
' Ausmaskieren von speziellen Symbolen im Präfix oder Suffix

Hier sehen wir ein Beispiel für die Auswirkungen der Formatanweisungen auf einige Zahlen:

Tabelle 4.25: Beispiel für verschiedene Formatanweisungen

Format Eingabezahl Ergebnis
0000 12 0012
0000 12,5 0012
0000 1234567 1234567
## 12 12
## 12.3456 12
## 123456 123456
.00 12.3456 12,35
.00 .3456 ,35
0.00 .789 0,79
#.000000 12.34 12,340000
,### 12345678.901 12.345.679
#.#;(#.#) 12345678.901 12345678,9
#.#;(#.#) –12345678.901 (12345678,9)
,###.## \u00A4 12345.6789 12.345,68 €
,#00.00 \u00A4\u00A4 –12345678.9 –12.345.678,90 EUR
,#00.00 \u00A4\u00A4 0.1 00,10 EUR

Währungen angeben und die Klasse Currency

Die NumberFormat-Klasse liefert mit getCurrencyInstance() ein Format-Objekt, das neben der Dezimalzahl auch noch ein Währungssymbol mit anzeigt. So liefert NumberFormat.getCurrencyInstance().format(12345.6789) dann 12.345,68 €, also automatisch mit einem Euro-Zeichen. Dass es ein Euro-Zeichen ist und kein Yen-Symbol, liegt einfach daran, dass Java standardmäßig das eingestellte Land »sieht« und daraus die Währung ableitet. Wenn wir explizit den Formatter mit einem Land initialisieren, etwa wie in

NumberFormat frmt1 = DecimalFormat.getCurrencyInstance( Locale.FRANCE );
System.out.println( frmt1.format( 12345.6789 ) ); // 12 345,68 €

so ist die Währung automatisch Euro (denn Frankreich nutzt den Euro); schreiben wir DecimalFormat.getCurrencyInstance(Locale.JAPAN), ist sie Yen, und wir bekommen ¥12,346 (es gibt standardmäßig keine Nachkommastellen beim Yen). Locale-Objekte repräsentieren immer eine Sprachregion.

DecimalFormat beziehungsweise schon die Oberklasse NumberFormat ermöglicht die explizite Angabe der Währung. In der Java-Bibliothek wird sie durch die Klasse java.util.Currency repräsentiert. NumberFormat liefert mit getCurrency() die eingestellte Currency, die zur Formatierung verwendet wird, und setCurrency() setzt sie neu. Das löst Szenarios, in denen etwa ein Euro-Zeichen die Währung darstellt, aber die Zahlenformatierung englisch ist, wie die folgenden Zeilen zeigen:

NumberFormat frmt = DecimalFormat.getCurrencyInstance( Locale.ENGLISH );
frmt.setCurrency( Currency.getInstance( "EUR" ) )
;
System.out.println( frmt.format( 12345.6789 ) ); // EUR12,345.68

Die Currency-Klasse bietet drei statische Methoden, die Currency-Objekte liefern. Da ist zum einen getAvailableCurrencies(), was ein Set<Currency> liefert, und zum anderen gibt es die beiden Fabrikfunktionen getInstance(Locale locale) und getInstance(String currencyCode). Currency-Objekte besitzen eine ganze Reihe von Objektfunktionen, die etwa den ISO-4217-Währungscode liefern oder den ausgeschriebenen Währungsnamen (und das auch noch in verschiedenen Sprachen, wenn gewünscht).

Folgendes Programm geht über alle Währungen und gibt die zentralen Informationen aus:

for ( Currency currency : Currency.getAvailableCurrencies() )
{
System.out.printf( "%s, %s, %s (%s)%n",
currency.getCurrencyCode(),
currency.getSymbol(),
currency.getDisplayName(),
currency.getDisplayName(Locale.ENGLISH) );
}

Wir bekommen dann mehr als 200 Ausgaben, und die Ausgabe beginnt mit:

EGP, EGP, Ägyptisches Pfund (Egyptian Pound)
IQD, IQD, Irak Dinar (Iraqi Dinar)
GHS, GHS, Ghana Cedi (Ghana Cedi)
AFN, AFN, Afghani (Afghani)
MUR, MUR, Mauritius Rupie (Mauritius Rupee)
SGD, SGD, Singapur Dollar (Singapore Dollar)
...

Rheinwerk Computing - Zum Seitenanfang

4.11.6 MessageFormat und Pluralbildung mit ChoiceFormatZur nächsten ÜberschriftZur vorigen Überschrift

MessageFormat ist eine besondere Unterklasse von Format, die für die Formulierung von Nachrichten gedacht ist. Die Klasse ist in etwa mit den Formatierungen über printf(), nur werden bei MessageFormat die Platzhalter immer per Index in geschweiften Klammern angesprochen.

Beispiel

Formuliere einen Nachrichtenstring mit drei Feldern:

int soldCars = 10;
double sum = 1234534534;
String s = MessageFormat.format( "{0} Auto(s) verkauft am {1,date} zum "+
"Gesamtpreis von {2,number,currency}.",
soldCars, new Date(), sum );
System.out.println( s );
Die Ausgabe ist:
»10 Auto(s) verkauft am 01.07.2011 zum Gesamtpreis von 1.234.534.534,00 €.«
Sie ist automatisch lokalisiert, die Sprache lässt sich jedoch wieder als Locale-Objekt übergeben.

ChoiceFormat

Eine besondere Möglichkeit ist die Verbindung von MessageFormat und ChoiceFormat, um das Problem zu lösen, das unser Beispiel im Fall von verkauften Autos mit »Auto(s)« löst. Im Deutschen ist die Pluralbildung anspruchsvoll, da es »0 Autos, 1 Auto, 2 Autos, 3 Autos« usw. heißt aber nur »0 Koffer, 1 Koffer, 2 Koffer, ...«. Das in Software zu modellieren ist nicht ganz einfach, aber mit ChoiceFormat lässt es sich lösen. Dem Konstruktor werden zum Generieren der Ausgabe zwei Felder mitgegeben: Ein Limit-Array kodiert Bereiche, und ein zweites Feld enthält die zugeordneten Elemente für den Bereich.

Beispiel

Löse das Problem mit »0 Autos, 1 Auto, 2 Autos« usw.:

MessageFormat formatter = new MessageFormat( "Du hast {0} {1} verkauft." );
double[] limits = { 0., 1., 2. };
String[] formats = { "Autos", "Auto", "Autos" };
ChoiceFormat choices = new ChoiceFormat( limits, formats );
formatter.setFormatByArgumentIndex( 1, choices );
int size = 4;

Object[] params = { size, size };
System.out.println( formatter.format( params ) ); // Du hast 4 Autos verkauft.
Das Feld {0., 1., 2.} interpretiert sich so: Liegt der Wert zwischen größer gleich 0 und echt kleiner 1 (also bei Ganzzahlen ist er effektiv 0), wird das erste Element des Feldes {"Autos", "Auto", "Autos"}, also »Autos«, gewählt. Liegt es zwischen größer gleich 1 und echt kleiner 2, dann ist es das »Auto«. Alles was echt größer 2 ist, wird auf »Autos« abgebildet. Für die 2 im Feld ist Math.nextUp(1.) eine Alternative, und wenn auf »Komma-Autos« gewechselt wird, ist es gleich korrekt, denn es heißt zwar »1 Auto« aber »1,5 Autos«. (Genau genommen sind es sogar »1,0 Autos«, wodurch die ganze Pluralbildung wegfällt.)

Die API-Dokumentation der Klasse ChoiceFormat gibt weitere Beispiele und zeigt insbesondere, wie sich die Bereiche auch in den Strings selbst kodieren lassen, was für externe Übersetzungsdateien optimal ist.



Ihr Kommentar

Wie hat Ihnen das <openbook> gefallen? Wir freuen uns immer über Ihre freundlichen und kritischen Rückmeldungen.

>> Zum Feedback-Formular
<< zurück
  Zum Katalog
Zum Katalog: Java ist auch eine Insel





Java ist auch eine Insel
Jetzt bestellen


 Ihre Meinung?
Wie hat Ihnen das <openbook> gefallen?
Ihre Meinung

 Buchempfehlungen
Zum Katalog: Java ist auch eine Insel






 Java ist auch
 eine Insel


Zum Katalog: Java SE Bibliotheken






 Java SE Bibliotheken


Zum Katalog: Professionell entwickeln mit Java EE 7






 Professionell
 entwickeln mit
 Java EE 7


Zum Katalog: Einstieg in Eclipse






 Einstieg in
 Eclipse


Zum Katalog: Einstieg in Java






 Einstieg in
 Java


 Shopping
Versandkostenfrei bestellen in Deutschland und Österreich
InfoInfo




Copyright © Rheinwerk Verlag GmbH 2011
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


[Rheinwerk Computing]

Rheinwerk Verlag GmbH, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, service@rheinwerk-verlag.de