3 Ways To Perform Java String Formatting

Generate pretty strings using printf(), format(), and Formatter class

Since I learned Java as one of my first object-oriented programming languages, one would think that I would be close to mastering Java by now. However, that’s not how it works in the world of programming languages, as they keep on releasing new stuff with each update.

I remember that the first time I learned Java, the version was definitely less than Java 8. Now it’s already passing version 12. However, sometimes there would be a major update in one or two of the versions, meaning that there would suddenly be a lot of new methods introduced in one release (as compared to other releases) that could help developers code more easily.

In today’s article, I will be talking about probably one of the seemingly simplest things, yet one that might be confusing. It is about Java String formatting.

You will probably have encountered String formatting early on. However, you might still be googling about it now, even though years have passed. Yes, that’s me. It seems like I kind of looked down on the simplicity of it, but the complexity behind it is the one thing that might really help in programming efficiently. Additionally, every time I tried to look for information about it, it seemed like the information was always all over the place. So I have decided to try to compile it all into one article in the simplest form of explanation.

Let’s get started!


Java String formatting returns the formatted string by the given format. There are what I consider three ways of doing it:

  1. System.out.printf()
  2. String.format()
  3. Formatter class

As you probably can notice, the three of them are basically different:

  • The first one is printing output.
  • The second one can be stored in a variable.
  • The last one is simply a class.

Let’s take a further look at them.


1. Printf()

This is a simple printout of a formatted string with a given format and arguments. This method is part of the java.io.PrintStream class.

Syntax:

System.out.printf(“string_format” [, arg1, arg2, …]);
System.out.printf(locale, “string_format” [, …args]);

Internally, printf() uses the java.util.Formatter class to parse the format string and generate the output.

The “string_format” consists of literals and format specifiers (formatting rules that start with %). What is a format specifier? Based on my own interpretation, it is like the thing that determines how your string will look, as the output of the string will be determined (specified) by flags, width, precision, and conversion characters. Arguments are the needed stuff when format specifiers are there as they are substituted by the conversion characters. We will see more with examples later.

Format specifiers have the following sequence:

%[flags][width][.precision][conversion-character]

The flags, width, and precision are optional. The conversion character itself is mandatory because it denotes what kind of arguments you want to substitute with.

The flags

These define standard ways to modify the output and are most common for formatting integers and floating-point numbers.

  • - left-justifies. (Right-justify is the default.)
  • outputs a plus ( + ) or minus ( – ) sign for a numerical value.
  • 0 forces numerical values to be zero-padded. (Spaces are the default.)
  • , is the comma grouping separator (for numbers > 1000).
  • [space] will display a minus sign if the number is negative or a space if it is positive.

The width

The width specifies the field width for outputting the argument. It represents the minimum number of characters to be written to the output. Include space for expected commas and a decimal point in the determination of the width for numerical values.

The precision

It specifies the number of digits of precision when outputting floating-point values. Additionally, we can use it to define the length of a substring to extract from a String. Numbers are rounded to the specified precision.

Conversion characters

  • d: decimal integer (byte, short, int, long)
  • f: floating-point number (float, double)
  • c: character (Capital C will uppercase the letter.)
  • s: String (Capital S will uppercase all the letters in the string.)
  • h: hashcode (A hashcode is like an address. This is useful for printing a reference.)
  • n: newline (Platform specific newline character — use %n instead of \n for greater compatibility.)

Despite all the options available in formatting a String, there is one format specifier that is used quite a lot, and that is the line separator, denoted by %n. This will create a new line.

Usually, in a normal String you can use \, but under the formatting method, it usually is %n.

Example:

System.out.printf("Mark%nZuck%nNewLine");
// Output
Mark
Zuck
NewLine

Some of the character conversions can return a value in capital letters. How? Simply capitalise the character like so:

System.out.println(“%b%n”, null);
System.out.println(“%B%n”, false);
// Output
false
FALSE

Notice how when the b is capitalised, the result is also capitalised.

Other examples:

// Long formatting
System.out.printf("%d%n", 888L);
// Integer formatting with comma
System.out.printf("%,d%n", 8888);
// Locale formatting
System.out.printf(Locale.US, "%d%n", 100000);
// Locale formatting with comma
System.out.printf(Locale.US, "%,d%n", 100000);
System.out.printf(Locale.ITALY, "%,d%n", 100000);
// String width formatting with 2 decimal places precision
System.out.printf("'%5.2f'%n", 8.2476);
// Integer width formatting
System.out.printf("'%3d'%n", 8);
// With hash code
String s = "Hello World";
System.out.printf("The String object %s has hash code %h%n", s, s);
// String width formatting left justified
System.out.printf("'%-6S'%n", "as");
// String width (determine the length) and precision (determine the allowed number of characters) formatting
System.out.printf("'%4.2s'", "asdfasdf");
// Output
888
8,888
100000
100,000
100.000
' 8.24'
'  8'
The String object Hello World has hash code cc969a84
'AS    '
'  as'

2. String.format()

This method is of the Formatter class as well. It is exposed via a static method from the String class. Additionally, the way it is used is similar to that of printf, with the exception that this one can be assigned to a variable.

With that said, this format method returns a reference to a String.

Syntax:

String.format(“string_format” [, arg1, arg2, ...]);
String.format(locale, “string_format” [, ...args]);

For a quick recap, the above consists of:

  • string_format — a format for the string
  • args — 0 or more arguments
  • Locale — specifies how a specific region is going to be formatted. Usually I use it more when numbers are involved, like how integers are displayed differently between Locale.US and Locale.ITALY based on the previous examples.

If you don’t specify the locale in your String.format() method, it uses default locale by calling Locale.getDefault() method.

Since it is similar to that of the printf method, it also follows the same formatting rules. The syntax of the format specifiers are as follows:

%[argument_index$][flags][width][.precision][conversion-character]

Where:

  • argument_index is an integer i — indicating that the ith position of the argument in the supplied argument list.
  • flags is a set of characters used for modifying the output format.
  • width is a positive integer which indicates the minimum number of characters to be written to the output.
  • precision is an integer usually used to restrict the number of characters, whose specific behavior depends on the conversion.
  • conversion-characteris a mandatory part that indicates what data type is replaced.

Let’s take a look at how argument_index works.

String greetWorld = String.format(“Hello %2$s, I am %1$s !”, “Index”, “World”);
// Output
Hello World, I am Index !

Notice how the syntax for the argument index (based on the example above) is denoted by 2$ (for the second argument) and 1$ (for the first argument) respectively.

Despite all these, let me share with you how you can still use the format() method and yet no formatting rule is applied.

String s = String.format(“It is an amazing 90%% chance!”);
System.out.pritnln(s);
// Output
It is an amazing 90% chance!

The sentence just popped up in my mind when I tried to think of a random example that is not the usual, so don’t ask me what or why. Weird stuff aside, notice how I didn’t use any format specifiers whatsoever. But why did I use two percent signs, %%? The reason is that since we are using the format() method, we use one more % to escape the character so that it appears as 90%.

Since the formatting options, flags, and stuff are pretty much the same as the printf method, you can refer to that section. So for this part, I will include more examples instead.

Examples:

// Octal format
String.format("%o", 47);
// Hexadecimal format
String.format("%x", 47);
String.format("%X", 47);
// String format
String.format("%s", 69);
String.format("%s", 85.868f);
// Scientific notation
String.format("%g", 44534345.76d);
// Decimal places format (with precision restriction)
String.format("%f", -452.534f);
String.format("%f", -345.766d);
String.format("%.2f", -452.534f);
String.format("%.2f", -345.766d);
// Output
57
2f
2F
69
85.868
4.45343e+07
-452.533997
-345.766000
-452.53
-345.77

When we format -452.534 using %f, we are getting 452.533997. It is not because of the format() method. Java doesn’t return the exact representation of floating-point numbers.

When the %.2f format specifier is used, format() gives two numbers after the decimal point.

Other examples:

// Padding number with spaces (default) with length of string 5
String result = String.format("|%5d|", 88);
// Padding number with 0 with length of string 5
String result = String.format("|%05d|", 88);
// using signs before numbers
String result = String.format("%+d", 88);
String result = String.format("%+d", -88);
// Enclose negative number with parenthesis to remove the sign
String result = String.format("%(d", n2);
String.format("%s", "Mark");
String.format("%S", "Zuck");
// Left-justifying within the specified width
String.format("|%-10d|", 101);
// Using the GERMAN locale
String.format(Locale.GERMAN, "%,d", 1001000);
// Output
|   88|
|00088|
+88
-88
88
Mark
ZUCK
|101       |
1.001.000

In Germany, integers are separated by a period instead of a comma.

There are also alternate representations for octal and hex output, which prints octal numbers with a leading “0” and hex numbers with leading “0x“.

String.format("|%#o|", 93);
// prints: 0135
String.format("|%#x|", 93);
// prints: 0x5d
String.format("|%#X|", 93);
// prints: 0X5D

With that said, here I list out some Java String format specifiers. Take note that this does not cover the entirety (refer to the documentation for the complete list).

Some format specifiers that I have seen/used/read.

3. Formatter Class

This class refers to java.util.Formatter class. For this case, we need to create a Formatter instance and use that to invoke the format() method.

Example:

StringBuilder sb = new StringBuilder();
Formatter formatter = new Formatter(sb);
formatter.format("I am writing to a %s instance.", sb.getClass());
System.out.println(sb.toString());
// You can continue to append data to sb here
// Output
I am writing to a class java.lang.StringBuilder instance.

As you can see from the example above, the Formatter instance is created and then linked to StringBuilder. Basically, the output of the format() method is appended to the StringBuilder object, which we then print out by converting it to a String using the toString() method.

You can then continue to append formatted stuff to your StringBuilder.

In terms of the formatting rules, they are the same as the other two, printf() and String.format(). Hence, you can just reuse the rules, and it is applicable here as well since the other two inherently use the Formatter class to format.


Wrap-Up

In this article, we saw the different formatting ways provided by the java.util.Formatter class. We also saw various syntaxes that can be used to format Strings and the conversion types that can be used for different data types. Hopefully you got to know them better.

That’s all for today’s learning. If you spotted something that I am missing in this article, feel free to share it in the comment section below. We would all love to learn together.

Thanks for reading.