The java.text
package
includes, among other things, a set of classes designed for generating and
parsing string representations of objects. In this section, we’ll talk
about three classes: NumberFormat
, ChoiceFormat
, and
MessageFormat
. Chapter 11 describes the DateFormat
class. As we said earlier, the
classes of the java.text
package
overlap to a large degree with the capabilities of the Scanner
and printf
-style Formatter
. Despite these new features, a number
of areas in the parsing of currencies, dates, and times can only be
handled with the java.text
package.
The NumberFormat
class can be
used to format and parse currency, percentages, or plain old numbers.
NumberFormat
is an abstract class, but
it has several useful factory methods that produce formatters for
different types of numbers. For example, to format or parse currency
strings, use getCurrencyInstance()
:
double
salary
=
1234.56
;
String
here
=
// $1,234.56
NumberFormat
.
getCurrencyInstance
().
format
(
salary
);
String
italy
=
// L 1.234,56
NumberFormat
.
getCurrencyInstance
(
Locale
.
ITALY
).
format
(
salary
);
The first statement generates an American salary, with a dollar
sign, a comma to separate thousands, and a period as a decimal point. The
second statement presents the same string in Italian, with a lire sign, a
period to separate thousands, and a comma as a decimal point. Remember
that NumberFormat
worries about format
only; it doesn’t attempt to do currency conversion. We can go the other
way and parse a formatted value using the parse()
method, as we’ll see in the next
example.
Likewise, getPercentInstance()
returns a formatter you can use for generating and parsing percentages. If
you do not specify a Locale
when
calling a getInstance()
method, the
default Locale
is used:
double
progress
=
0.44
;
NumberFormat
pf
=
NumberFormat
.
getPercentInstance
();
System
.
out
.
println
(
pf
.
format
(
progress
)
);
// "44%"
try
{
System
.
out
.
println
(
pf
.
parse
(
"77.2%"
)
);
// "0.772"
}
catch
(
ParseException
e
)
{}
And if you just want to generate and parse plain old numbers, use a
NumberFormat
returned by getInstance()
or its equivalent, getNumberInstance()
:
NumberFormat
guiseppe
=
NumberFormat
.
getInstance
(
Locale
.
ITALY
);
// defaults to Locale.US
NumberFormat
joe
=
NumberFormat
.
getInstance
();
try
{
double
theValue
=
guiseppe
.
parse
(
"34.663,252"
).
doubleValue
();
System
.
out
.
println
(
joe
.
format
(
theValue
));
// "34,663.252"
}
catch
(
ParseException
e
)
{}
We use guiseppe
to parse a number
in Italian format (periods separate thousands, comma is the decimal
point). The return type of parse()
is
Number
, so we use the doubleValue()
method to
retrieve the value of the Number
as a
double
. Then we use joe
to format the number correctly for the
default (U.S.) locale.
Here’s a list of the factory methods for text formatters in the
java.text
package. Again, we’ll look at
the DateFormat
methods in the next
chapter.
NumberFormat
.
getCurrencyInstance
()
NumberFormat
.
getCurrencyInstance
(
Locale
inLocale
)
NumberFormat
.
getInstance
()
NumberFormat
.
getInstance
(
Locale
inLocale
)
NumberFormat
.
getNumberInstance
()
NumberFormat
.
getNumberInstance
(
Locale
inLocale
)
NumberFormat
.
getPercentInstance
()
NumberFormat
.
getPercentInstance
(
Locale
inLocale
)
DateFormat
.
getDateInstance
()
DateFormat
.
getDateInstance
(
int
style
)
DateFormat
.
getDateInstance
(
int
style
,
Locale
aLocale
)
DateFormat
.
getDateTimeInstance
()
DateFormat
.
getDateTimeInstance
(
int
dateStyle
,
int
timeStyle
)
DateFormat
.
getDateTimeInstance
(
int
dateStyle
,
int
timeStyle
,
Locale
aLocale
)
DateFormat
.
getInstance
()
DateFormat
.
getTimeInstance
()
DateFormat
.
getTimeInstance
(
int
style
)
DateFormat
.
getTimeInstance
(
int
style
,
Locale
aLocale
)
Thus far, we’ve seen how to format numbers as text. Now, we’ll take
a look at a class, ChoiceFormat
, that maps
numerical ranges to text. ChoiceFormat
is constructed by specifying the numerical ranges and the strings that
correspond to them. One constructor accepts an array of double
s and an array of String
s, where each string corresponds to the
range running from the matching number up to (but not including) the next
number in the array:
double
[]
limits
=
new
double
[]
{
0
,
20
,
40
};
String
[]
labels
=
new
String
[]
{
"young"
,
"less young"
,
"old"
};
ChoiceFormat
cf
=
new
ChoiceFormat
(
limits
,
labels
);
System
.
out
.
println
(
cf
.
format
(
12
));
//"young"
System
.
out
.
println
(
cf
.
format
(
26
));
// "less young"
You can specify both the limits and the labels using a special
string in an alternative ChoiceFormat
constructor:
ChoiceFormat
cf
=
new
ChoiceFormat
(
"0#young|20#less young|40#old"
);
System
.
out
.
println
(
cf
.
format
(
40
));
// old
System
.
out
.
println
(
cf
.
format
(
50
));
// old
The limit and value pairs are separated by vertical bars (|
); the number sign (#
) separates each limit from its corresponding
value.
ChoiceFormat
is most useful for
handling pluralization in messages, enabling you to avoid hideous
constructions such as, “you have one file(s) open.” You can create
readable error messages by using ChoiceFormat
along with the MessageFormat
class.
MessageFormat
is a
string formatter that uses a pattern string in the same way that
printf()
formatting does. MessageFormat
has largely been replaced by
printf()
, which has more options and
is more widely used outside of Java. Nonetheless, some may still prefer
MessageFormat
’s style, which is a bit
less cryptic than that of printf()
.
MessageFormat
has a static formatting
method, MessageFormat.format()
,
paralleling the print-style formatting of String.format()
.
Arguments in a MessageFormat
format string are delineated by curly brackets and may include
information about how they should be formatted. Each argument consists
of a number, an optional type, and an optional style, as summarized in
Table 10-8.
Let’s use an example to clarify this:
//Equivalent to String.format("You have %s messages.", "no");
MessageFormat
.
format
(
"You have {0} messages."
,
"no"
);
The special incantation {0}
means “use element zero of the arguments supplied to the format()
method.” When we generate a message
by calling format()
, we pass in
values to replace the placeholders ({0}
, {1}
,
... ) in the template. In this case, we pass the string “no” as arguments[0]
, yielding the result, You have no messages.
Let’s try this example again, but this time, we’ll format a number and a date instead of a string argument:
MessageFormat
mf
=
new
MessageFormat
(
"You have {0, number, integer} messages on {1, date, long}."
);
// "You have 93 messages on April 10, 2002."
System
.
out
.
println
(
mf
.
format
(
93
,
new
Date
()
)
);
In this example, we need to fill in two spaces in the template, so
we need two arguments. The first must be a number and is formatted as an
integer. The second must be a Date
and is printed in the long
format.
This is still sloppy. What if there is only one message? To make
this grammatically correct, we can embed a ChoiceFormat
-style pattern string in our
MessageFormat
pattern string:
MessageFormat
mf
=
new
MessageFormat
(
"You have {0, number, integer} message{0, choice, 0#s|1#|2#s}."
);
// "You have 1 message."
System
.
out
.
println
(
mf
.
format
(
1
)
);
In this case, we use the first argument twice: once to supply the
number of messages and once to provide input to the ChoiceFormat
pattern. The pattern says to add
an s
if the argument has the value
0
or is 2
or more.
When writing internationalized programs, you can use resource
bundles to supply not only the text of messages, but also the format
strings for your MessageFormat
objects. In this way, you can automatically format messages that are in
the appropriate language with dates and other language-dependent fields
handled appropriately and in the appropriate order. Because arguments in
the format string are numbered, you can refer to them in any location.
For example, in English, you might say, “Disk C has 123 files”; in some
other language, you might say, “123 files are on Disk C.” You could
implement both messages with the same set of arguments:
MessageFormat
m1
=
new
MessageFormat
(
"Disk {0} has {1, number, integer} files."
);
MessageFormat
m2
=
new
MessageFormat
(
"{1, number, integer} files are on disk {0}."
);
In real life, the code could be more compact; you’d use only a
single MessageFormat
object,
initialized with a string taken from a resource bundle. Or you’d likely
want to use the static format
method
or switch to printf()
entirely.
Get Learning Java, 4th Edition now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.