Comprehensive Guide to Java String Formatting

Formatting Strings in Java can be a confusing and difficult task for beginners and experts alike. While the concept of String formatting is simple, there are nearly endless combinations that can be easy to forget and frustrating to look up. In this article, we will outline the basic techniques for formatting Strings in Java using the venerable C-style and walk through easy-to-digest tables for all of the conversions and format specifiers available to us (that can be used as reference tables when we inevitably forget which format specifiers to use).

While not the focus of this article, we will also touch on the MessageFormat class and how it can provide a simplified approach when the C-style approach is too cumbersome. Additionally, we will look at the various mechanisms we can use to format C-style conversions, including flags, widths, and precisions. Lastly, we will cover some important references that will help us to gain a deeper understanding of formatting Strings in Java.

Approaches to Formatting Strings in Java

Java was created at a time when C and C++ were the top-dogs in the programming arena, and, unsurprisingly, formatting Strings in Java resembles the style common in C. Since this is the oldest and most versatile approach provided by Java, we will focus on how we can use these formatting methods to create the Strings we desire. Nonetheless, a significant time has passed since the inception of Java and other approaches have been included in the language that trades the versatility of the C-style formatters for simplicity. One of the most common alternatives is the MessageFormat class. Although we will not focus on this class, we will touch on its basics and understand how it can be used as an alternative to the C-style approach for a wide variety of situations.

Methods and Classes

There are three common C-style methods used for formatting Java String objects:

  1. PrintStream.format : Formats a String object and prints it to a stream, such as Standard Output.
  2. String.format: Formats a String object and returns it as a new String object; this allows the results to be assigned to a variable.
  3. Fomatter.format: Formats a String object and writes it to the destination supplied in the constructor of the Formatter object.

When applicable, we can also use MessageFormat.format, which acts as a simplified version of the full-featured String formatting supported by the above format methods.

PrintStream.format

Each time we call System.out, we expect that we obtain a reference to standard output. If we look closely at this call, System.out simply provides a PrintStream object that maps to standard output. Therefore, we can write to standard output by calling the format method of the PrintStream class (i.e., System.out.format). We can also format Strings and write them to the other standard PrintStreamSystem.err (standard error) — in a similar manner. For example, we can write a formatted String to standard error using the System.err.format method. 

Note that the PrintStream class also provides a printf method that is identical to the format method, but keeps the printf naming familiar to C developers. Adding these options together, we get four different ways to write formatted Strings to the various standard PrintStreams:

  1. System.out.format
  2. System.err.format
  3. System.out.printf
  4. System.err.printf

String.format

Unlike the PrintStream variants, String.format does not write the formatted String directly to a stream (such as standard output). Instead, the String.format returns a new String that is the result of the formatting operation. For example:

Java
 
String formattedString = String.format(...);


While the the PrintStream variants will be useful for beginners and those dealing directly with standard output, standard input, and standard error, it will be more common to format a String using the String.format method and capture the results in a new String for use at a later time.

Formatter

The Formatter.format method is similar to the String.format method, but instead of returning the resulting String object, the Formatter.format method writes the resulting String object to the destination supplied in its constructor. The possible types of destinations include:

For example, we can write a formatted String to a file foo.txt using the following:

Java
 
File file = new File("foo.txt");
Formatter formatter = new Formatter(file);
formatter.format("Hello, %s%n", "Justin");
formatter.close();


If we look at the contents of the file foo.txt, we see that it contains our formatted String:

Plain Text
 
Hello, Justin


We will see shortly what %s and %n mean and how the format method works, but for now, we can see that we can write formatted Strings to a wide variety of different output destinations using the Formatter.format method. In many cases, the String.format method suffices by returning the formatted String — and we will focus primarily on this method throughout this article — but there are times when the flexibility provided by the Formatter.format method can be useful.

MessageFormat.format

Unlike the previous three approaches, the MessageFormat.format method does not provide an open-ended mechanism that accepts any C-style formatters but instead provides a simplified formatting structure that allows us to specify which argument a specifier corresponds to and which pre-packaged format to use for each argument. For example, if we want to print the first argument — which is addressed using 0-indexing — as an integer and the second as a date, we can use the following:

Java
 
String formattedString = MessageFormat.format("Int: {0,number,integer}, date: {1,date}", 117, new Date());


This results in the following String being store to formattedString (the particular date will vary based on when the above snippet is run, but the formatting of the date will remain the same):

Plain Text
 
Int: 117, date: Jul 14, 2021


We can also store a MessageFormat instance for reuse using the following:

Java
 
MessageFormat format = new MessageFormat("Int: {0,number,integer}, date: {1,date}");
format.format(new Object[] { 117, new Date() });


This snippet produces the same result as our previous example. Note that the arguments must be supplied as an Object[] when using the instance format method. While the MessageFormat class can be useful for concatenating various arguments into a single String, it is limited in its ability to format different values. Unlike the previous three methods, the MessageFormat approach does not provide the fine-grained options to format a String precisely how we want. This results in an approach that is easier to use but also less robust.

In this article, we will focus on the C-style String formatting provided in Java. However, it is still important to understand how the MessageFormat class can be useful in a wide variety of situations and how it can be used to format Strings in a simplified manner. For more information, see the official MessageFormat documentation. 

Structure for C-Style Strings in Java

Regardless of which method we choose, formatting a String in Java using the C-style requires two parts:

  1. Format String: A String that specifies the format the resulting String should have. Format strings are a combination of fixed text and one or more format specifiers. Format specifiers act as placeholders and are replaced with the values provided in the argument list according to the formatting options we provide.
  2. Argument List: A list of values that will be used to compose the format string according to format specifiers provided.

For example:

Java
 
String formattedString = String.format("Hello, %s", "Justin");


When we execute the above snippet, the result Hello, Justin is stored in the formattedString variable. In this case, the String Hello, %s is our format string, while Justin is the only argument in the argument list. In particular, the format specifier %s acts as a placeholder for strings. Since we provided a %s format specifier in our format string, %s was replaced by our Justin String in the argument list, resulting in a formatted String of Hello, Justin.

Format Specifier in Action

In this case, there is a one-to-one correspondence between the format specifier and the arguments in the argument list (i.e., the first argument is in the argument list is used to replace the first format specifier). This is not always the case, though. The correspondence between the format specifiers we provide and their replacement with arguments from the argument list depends on the particular format specifiers we use. 

If a specifier requires an argument and one is not provided, a MissingFormatArgumentException is thrown. For example, the following throws an MissingFormatArgumentException:

Java
 
String.format("%s");


In general, there are three categories of format specifiers we can use:

  1. General, characters, and numeric types
  2. Dates and times
  3. Specifiers that do not require an argument

The Java data types that correspond to each of these categories are specified in the table below:

Category Subcategory Data Types
General, characters, numeric types
General
Any type
Character
char, Character, byte, Byte, short, Short, and int and Integer when Character.isValidCodePoint(int) results in true
Integer
byte, Byte, short, Short, int, Integer, long, Long, and BigInteger
Floating Point
float, Float, double, Double, and BigDecimal
Dates and times
-
long, Long, Calendar, and Date.
Non-argument specifiers
-
-

Formatting General, Character and Numeric Strings

For general, character, and numeric types, format specifiers have the following format:

Plain Text
 
%<index$><flags><width><.precision><conversion>


Where:


Using our s conversion, all of the following are examples of valid format specifiers:

Plain Text
 
%s
%10s
%3$10s
%3$-10s


Conversions

A complete list of conversions is contained in the table below:

CONVERSION

DATA TYPE

RESULT

EXAMPLES

b, B

General

The boolean result as a string (e.g., true or false) if the supplied argument is a Boolean. false if the supplied argument is null and true for all other arguments. If B is used, the resulting boolean value is capitalized (e.g., TRUE or FALSE). true
false
TRUE
FALSE

h, H

General

The hexadecimal value of the supplied argument, arg, by applying the expression Integer.toHexString(arg.hashCode()). Note that the result does not include a 0x prefix. If H is used, the resulting alphabetic hexadecimal characters are capitalized. fff03
6199ec4
6199EC4

s, S

General

The provided argument as a string (by invoking Object.toString). If the supplied argument is a Formattable object, the string is obtained by invoking Formattable.formatTo. If S is supplied, the supplied string is capitalized. foo
bar
BAR

c, C

Character

The Unicode character. If a String object is supplied, an IllegalFormatConversionException is thrown. If C is used, the supplied character is capitalized. c
f
F

d

Integer

The decimal integer. 10
5347

o

Integer

The octal integer. 12
151

x, X

Integer

The hexadecimal integer. Note that the result does not include a 0x prefix. This specifier should be preferred over h when supplying an integer. If X is used, the resulting alphabetic hexadecimal characters are capitalized. ff4
e56d2
E56D2

e, E

Floating Point

The value in scientific notation. If E is used, the exponent symbol (e) is capitalized (i.e., E). 1.114313e+09
1.682340e-08
1.682340E-08

f

Floating Point

The decimal number. 4.562340
1245.643876

g, G

Floating Point

The value in scientific notation or decimal format, depending on the result after rounding. If G is used, the exponent symbol (e) is capitalized (i.e., E). 128636
8.12935e+16
3546.50
8.12935E+16

a, A

Floating Point

The value in scientific notation, where the significand is a hexadecimal floating point value, and the power follows a p delimiter. If A is used, the resulting alphabetic hexadecimal characters are capitalized and the p delimiter is capitalized (i.e., P). 0x1.63c774a3d70a4p19
0x1.187ff0337c5abp-26
0X1.8D7025536252DP45

Examples

The following table contains examples of various conversions, flags, indexes, precisions, and widths that demonstrate how we can use format specifiers to obtain our desired String. The table is constructed with the left column representing the format string supplied to the String.format method, the center column representing the argument list supplied to String.format, and the right column representing the result (i.e., the formatted String) returned from the String.format method:

Java
 
String <result> = String.format(<format-string>, <arguments...>);


Note that the format strings and results in the table below are surrounded by quotes to represent the String returned from the String.format method and highlighted any formatting, such as trailing spaces, that are included in the String.

Format String Arguments Result
"Hello, my name is %s" "Justin" "Hello, my name is Justin"
"Name: %10s" "Justin" "Name:     Justin"
"Name: %-10s" "Justin" "Name: Justin    "
"%f times %f is %.2f" 1.5, 6.784, 10.176 "1.500000 times 6.784000 is 10.18"
"%d+%d=%d" 2, 2, 4 "2+2=4"
"The speed of light is %.3g m/s" 299792458.0 "The speed of light is 3.00e+08 m/s"
"The first letter of the alphabet is %c" 'a' "The first letter of the alphabet is a"
"The hex value of %d is 0x%1$h" 8756 "The hex value of 8756 is 0x2234"

Formatting Date and Time Strings

Format specifiers for dates and times are similar to the generalized case above but are more constrained. Date and time format specifiers have the following format:

Plain Text
 
%<index$><flags><width><conversion>


The %, <index>, <flags>, and <width> components have the same meaning as they did previously, but the <conversion> is treated differently. To denote that the conversion should be a date or time, the character t or T is prepended to the conversion. This t or T prefix is followed by the format character for the date or time. Thus, the structure for a date or time format conversion is either:

Plain Text
 
t<date-time-conversion>
T<data-time-conversion>


For example, we can print a full month name using the B conversion (we will see a complete list of date and time conversions shortly):

Plain Text
 
%tB
%TB


When using a date or time conversion, the supplied argument must be a Java data type that encodes a date or time, such as a long, Long, Calendar, or Date. This argument is then formatted according to the supplied conversions. For example, we can use the date or time conversions, H an M, which represent the hour (in 24-hour format) and minutes, respectively — both with leading zeros if necessary — to format a supplied Date object:

Java
 
String.format("%1$tH %1$tM", new Date());


This snippet outputs the following (or similar, depending on the time it is executed):

Plain Text
 
08 50


By using the 1$ index, we instruct the format method to reuse our single argument (i.e., new Date()) when substituting both format specifiers. We can also use the %<$ to denote that the index of the previous argument used (hat tip to Robert Yacobellis for this information — originally sourced from Java By Comparison). For example, the following is equivalent to the previous example:

Java
 
String.format("%1$tH %<$tM", new Date());


Note that  %<$ will match the index of the previous format specifier. For example, if we have %2$ followed by %<$, %<$ will be equivalent to %2$. We can remove the indices altogether by storing our argument in a variable and supplying it for each of the arguments (in our case, twice):

Java
 
Date d = new Date();
String.format("%tH %tM", d, d);


Both approaches can be clunky and cryptic. We can use a DateTimeFormatter (or similar class, such as SimpleDateFormat) to format dates and times in most cases. When possible, using a DateTimeFormatter (or similar class) is preferable, since the format can be stored and reused and the formatting is more direct, since we already know that we intend to format a single date (and do not have to include any t or T prefixes or indices). For example, we can format the above date using a SimpleDateFormat object without the need to index the argument list:

Java
 
SimpleDateFormat format = new SimpleDateFormat("HH mm");
String formattedString = format.format(new Date());


This snippet results in formattedString storing the same hours and minutes as our previous example (note that SimpleDateFormat has its own specifiers and the format specifiers HH and mm are different than the %H and %M in our previous example). However, we no longer need to use the format specifier prefix (%), indices, or repeated arguments in the argument list.

Conversions

Nonetheless, dates and times can still be formatted through the C-style format methods, so we must know how to format them using conversions.

Date

The complete list of date conversions is contained in the table below:

CONVERSION RESULT EXAMPLES

B

Full-month name. The specific name will vary by locale. If the prefix T is used, the result is capitalized. For example, the format specifier %TB results in a capitalized month name. January
June
JUNE

h, b

Abbreviated name of the month. This specific name will vary by locale. If the prefix T is used, the result is capitalized. For example, the format specifiers %Th and %Tb result in a capitalized, abbreviated month names. Jan
Jun
JUN

A

Full day of the week. The specific day name will vary by locale. If the prefix T is used, the result is capitalized. For example, the format specifier %TA results in a capitalized day name. Monday
Wednesday
WEDNESDAY

a

Abbreviated day of the week. The specific day name will vary by locale. If the prefix T is used, the result is capitalized. For example, the format specifier %Ta results in a capitalized, abbreviated day name. Mon
Wed
WED

C

Four-digit year, divided by 100, padded with leading zeroes when necessary. This value ranges from 00 to 99.

For example, 19 for 1999 and 20 for 2000. This value can be thought of as the first two digits in the year, but note that all of the corresponding rules in Java for division apply here as the first two digits are obtained through division by 100.
19
20

Y

Four-digit year, formatted with leading zeroes when necessary. For example, the year 307 will result in 0307. 2021
0307

y

Two-digit year, ranging from 00 to 99. For example, 21 for 2021. If needed, the year will be padded with zeroes. For example, 05 for 2005. 21
05

j

Day of the year, formatted using three digits, including padding with zeroes when necessary. This value ranges from 001 (the first day of the year) to 366 for the Gregorian Calendar. 005
147

m

Two-digit month, including leading zeroes when necessary. This value ranges from 01 (the first month) to 13. 03
10

d

Two-digit day of the month, including leading zeroes when necessary. This value ranges from 01 (the first day of the month) to 31. 04
29

e

Two-digit day of the month. This value ranges from 1 (the first day of the month) to 31. 3
17

Time

Similarly, a complete list of time conversions are specified in the table below:

CONVERSION RESULT EXAMPLES
H Hour of the day in 24-hour format (i.e., military time) with leading zeroes when necessary. This value ranges from 00 to 23. 03
22
I Hour of the day in 12-hour format with leading zeroes as necessary. This value ranges from 01 to 12. 03
10
k Hour of the day in 24-hour format. This value ranges from 0 to 23. 3
22
l Hour of the day in 12-hour format. This value ranges from 0 to 12. 3
10
M Minute of the hour with leading zeroes when necessary. This value ranges from 00 to 59. 07
32
S Seconds of the minute with leading zeroes when necessary. This value ranges from 00 to 60, where 60 is a special value reserved for lead seconds. 06
24
L Three-digit milliseconds of the second with leading zeroes when necessary. This value ranges from 000 to 999. 040
748
N Nine-digit nanoseconds of the millisecond with leading zeroes when necessary. This value ranges from 000000000 to 999999999. 038518000
749300198
p Morning or afternoon marker (i.e., am or pm). This marker is locale-specific.

This marker can also be formatted to uppercase when used with the T prefix. For example, using the format specifier %tp produces either am or pm, while the format specifier %Tp produces either AM or PM.
am
pm
AM
PM
z Numeric time zone offset from GMT. This offset follows the RFC 822 format is adjusted as necessary for Daylight Savings Time (DST). For non-time-zoned arguments, such as long, Long, and Date, the default time zone for the current instance of the Java Virtual Machine (JVM) is used.

For more information on the default time zone, see TimeZone.getDefault().
-0800
+0400
Z Abbreviation for the time zone. This offset is adjusted as necessary for DST. For non-time-zoned arguments, such as long, Long, and Date, the default time zone for the current instance of the JVM is used. Note that the supplied locale will supersede the locale of the corresponding argument being formatted in the argument list when a locale is provided. EDT
GMT
s Seconds since the beginning of the epoch starting January 1, 1970 at 00:00:00 UTC. This value ranges from Long.MIN_VALUE/1000 to Long.MAX_VALUE/1000. 1626009506
1626010943
Q Milliseconds since the beginning of the epoch starting January 1, 1970 at 00:00:00 UTC. This value ranges from Long.MIN_VALUE to Long.MAX_VALUE. 1626009506640
1626010969000

Date and Time

There are also convenience conversions that combine common date and time formats into a single format specifier. The complete list of these conversions is contained in the table below:

CONVERSION Equivalent Specifier EXAMPLES
R %tH:%tM 09:18
T %tH:%tM:%tS 09:18:26
r %tI:%tM:%tS %Tp 09:18:26 AM
D %tm/%td/%ty 07/11/21
F %tY-%tm-%td (i.e., ISO 8601 complete date) 2021-07-11
c %ta %tb %td %tT %tZ %tY Sun Jul 11 09:18:26 EDT 2021

Examples

Standard conversions can be straightforward since the conversion simply follows the % prefix, but date and time conversions can often be confusing. Instead of using the conversion after the % prefix, we must first append the date/time prefix, t or T, and then append the desired date or time conversion. In essence, when dealing with date and time formatting, the prefix can be thought of as %t or %T, rather than just %.

For example, if we want to format the number of seconds since the January 1, 1970 epoch, we must use the %ts format specifier and supply a date-based value (such as a long or Date):

Java
 
Date date1 = new Date();
long date2 = 1626009506640L;

String formattedString1 = String.format("%ts", date1);
String formattedString2 = String.format("%ts", date2);


This snippet results in the following values:

Similarly, we can also the 24-hour time — hours and minutes — using the following snippet:

Java
 
Date date = new Date();

String formattedString = String.format("%1$tH %1$tM", date);


This results in the following value for formattedString:

Plain Text
 
10 32


Note that we have to provide the 1$ index to the format specifiers to provide only one argument (as we saw previously). Otherwise, the JVM would assume that our second format specifier (%tM) corresponds to the second argument in the argument — which does not exist. While we provided the index to both format specifiers, we are only required to prepend it to format specifiers other than the first one (in our case, the second format specifier). For example, the following snippet is equivalent to the String.format expression from our previous example:

Java
 
String formattedString = String.format("%tH %1$tM", date);


While date and time conversions can seem cryptic and complicated compared to the standard conversions, they follow nearly the same structure, but instead of using % only as a prefix, we use %t or %T.

Formatting Non-Argument Specifiers

There also exist specifiers that do not take arguments, such as % and n, which print the percent character and a platform-specific new line, respectively. These non-argument specifiers have the following format:

Plain Text
 
%<flags><width><conversion>


The <flags> and <width> components have the same meaning as in the general and date case, while the <conversion> is reserved for the set of conversions that do not require (or accept) accompanying arguments. The complete list of these conversions is contained in the following table:

CONVERSION

RESULT

EXAMPLES

%

The literal % character. I.e., String.format("%%") results in the string %. %

n

Platform-specific line separator (new-line). This conversion should be favored over using \n or or \r\n. Hello\nWorld
Hello\r\nWorld

 As an example, we can store a String with a percent character and a new line using the following:

Java
 
String formattedString = String.format("100%% complete.%nDone.");


This snippet results in the following String being stored in formattedString (for Linux):

Plain Text
 
100% complete.\nDone.


Note that we do not provide an argument list to the String.format call. Any arguments provided in an argument list for a non-argument specifier are ignored. For example, the following snippet will produce the same result — the String 100% complete.\nDone. being stored to formattedString — as our previous example:

Java
 
String formattedString = String.format("100%% complete.%nDone.", "foo");


Additional Java String Formatting

Apart from conversions, we can also format Java Strings using flags, widths, and precisions.

Flags

Flags act as general formatters that apply to various subsets of categories (e.g. general, date, and non-argument). It is important to note that not all flags work with every type of argument. The following table enumerates all of the available flags, as well which argument types to which they apply (where Y denotes that a flag applies to all conversions in that subcategory):

Flag Description General Character
Integer
Floating-
Point
Date
-
Left-justifies the results. Note that this flag must be accompanied by a width or a MissingFormatWidthException will be thrown.
Y
Y
Y
Y
Y
#
Uses an alternative form. The specific alternate form depends on the conversion. For more information, see the Details section of Formatter class.
Y
-
For o, x, and X
Y
-
+
Always includes a sign.
-
-
For d, o, x, and X applied to BigDecimal; or d applied to byte, Byte, short, Short, int, Integer, long, and Long
Y
-
 
(space)
Add leading space for positive values.
-
-
For d, o, x, and X applied to BigDecimal; or d applied to byte, Byte, short, Short, int, Integer, long, and Long
Y
-
0
Zero-pads the result.
-
-
Y
Y
-
,
Includes locale-specific grouping separators.
-
-
For d conversion
For e, E, f, g, and G conversion
-
(
Encloses negative numbers in parenthesis.
-
-
For d, o, x, and X applied to BigDecimal; or d applied to byte, Byte, short, Short, int, Integer, long, and Long
For e, E, f, g, and G conversion
-

It is important to note that more than one flag can be used at a time. For example, if we want to left-justify a numeric value while also including the sign, we can use the -+ flag (and a width, which we will specify as 10 for this example):

Java
 
String formattedString = String.format("%-+10d", 1745);


This results in the following being stored to formattedString (quotes added to emphasize the trailing spaces due to padding):

Plain Text
 
"+1745     "


Width

The width of a format specifier defines its minimum size. If the supplied argument does not contain enough characters after formatting, spaces are used to fulfill the minimum width. For example, if the format specifier %10s is used and the String name is supplied, six trailing spaces will be added to pad the result to fulfill the minimum width of 10 characters. If the - flag is used to left-justify the result, the whitespace is prefixed to the beginning of the resulting String. For example:

Java
 
String formattedString1 = String.format("%10s", "name");    // Result: "name      "
String formattedString2 = String.format("%-10s", "name");   // Result: "      name"


Precision

The meaning of precision depends on the conversion:

More Information

For more detailed information about C-style String formatting in Java, see the following:

Conclusion

Formatting Strings in Java can be tedious and difficult to remember. While there are a nearly unlimited number of combinations available to us, it is important that we understand the basics of how these various conversions, flags, widths, and precisions can be combined to form Strings. It is almost inevitable that we will have to look up specific conversions or flags — even after years of practice — but it is essential that we have a foundational knowledge of how Java Strings are formatted and how we can combine the various components of the C-style methods to create the Strings we desire.

 

 

 

 

Top