Java printf() method
The print() and println() methods on Java are very useful when printing data to an output console, but they are limited when it comes to applying any kind of special formatting to that data line alignment, widths and specific date formats to mention a few. The printf() method comes with a range of format specifiers and sub-specifiers that give developers a comprehensive option list to choose from when displaying data.
There are two general syntax options for the printf() method:
- System.out.printf(string); //equivalent to the print() method
- System.out.printf(format, arguments);
1. printf(string);
The first, System.out.printf(string); is equivalent to the print() method you are likely to be already familiar with. This simply prints whatever string is contained within the methods argument. For example, let’s use both methods to output the name “Learning Glue”:
System.out.print("Learning Glue"); System.out.printf("Learning Glue");
The output will be:
run: Learning GlueLearning GlueBUILD SUCCESSFUL (total time: 0 seconds)
Notice how the output is a little hard to read. This is because both the print() and printf() methods do not include any new line instruction (unlike the println() method) and both strings run after each other in the output. Let’s fix that by using the \n escape character to insert a new line which works for both methods:
System.out.print("Learning Glue\n"); System.out.printf("Learning Glue\n");
Now we have a more orderly output:
run: Learning Glue Learning Glue BUILD SUCCESSFUL (total time: 1 second)
The escape character \n is one of many that we will explore in this course, but for the printf() method we have anther option that will perform the same new line action, the %n.
System.out.print("Learning Glue\n"); System.out.printf("Learning Glue%n");
This give us the same output:
run: Learning Glue Learning Glue BUILD SUCCESSFUL (total time: 1 second)
In much the same way as the \ character, the % character acts as a placeholder in a printf() method. When a % character is encountered, the printf() method reads the character(s) following the % character to determine what to do, as we have seen with the %n (and \n) examples above where the character n results in a new line. This brings us to the second printf() method option: System.out.printf(format, arguments);
2. printf(format, arguments);
This printf() method option contains one or more format specifiers typically followed by the arguments to be formatted. We have already met one format specifier, the %n, which will insert a new line and does not require an argument.
Let’s have a look at a straight forward println() method and then use a printf() method with a format modifier and argument to create the same output:
String studentName = "Andrew"; System.out.println(studentName + " is a student at this College."); System.out.printf("%s is a student at this College.%n", studentName);
When you run this code you should get this output:
run: Andrew is a student at this College. Andrew is a student at this College. BUILD SUCCESSFUL (total time: 1 second)
We can see that the println() method uses a variable concatenated with a string which is a typical use of that method. The printf() method has a very different structure. While it also uses variables to display content within a string, the variable name is listed in the second part of the statement and is referred to using a format specifier in the first part:
System.out.printf(“%s is a student at this College.%n“, studentName);
The % character is a placeholder and acts like a flag that Java will pay attention to, and the letter after the % character will determine how and what Java will place at that point in the outputted string. In this example, the letter after the % character is an s, and %s is the format specifier for a String – so Java will insert a Sting at this point, and the String will be the first argument it sees in the second part of the statement – in this case the variable studentName. For completeness, we see another % character in this example, the %n, and this is a format specifier for a new line.
In this simple, single argument, example we can see the basic building block of the printf() method and how it maps to the general syntax of printf(format, arguments).
Exercise
Let’s be sure we fully understand this logic before we move on. Write a program the will store your favourite colour and, taking my favourite colour (blue) as an example, print out the statement “My favourite colour is blue, how about you?” using the printf() method.
If you have finished or are stuck go ahead and click here to revel the solution...
String myFavouriteColour = "blue"; System.out.printf("My favouite colour is %s, how about you?%n", myFavouriteColour);
This will output:
run: My favouite colour is blue, how about you? BUILD SUCCESSFUL (total time: 1 second)
Now that we understand the basic mechanics of the printf(format, arguments) method let’s take it further by exploring other format specifiers, sub-specifiers, escape characters and time formatting. Let’s start with the full list of format specifiers:
Format Specifiers
- %c character
- %d decimal (integer) number (base 10)
- %e exponential floating-point number
- %f floating-point number
- %i integer (base 10)
- %o octal number (base 8)
- %s String
- %u unsigned decimal (integer) number
- %x number in hexadecimal (base 16)
- %t formats date/time
- %% print a percent sign
- \% print a percent sign
- %n print a new line
- \n print a new line
- \t print a tab space
We have met a couple of these already, the %s for a String and %n for a new line. Together with the character (%c), decimal (%d), floating point number (f), and time/date (%t), these are the most common format specifiers you will use with the printf() method. Let’s start by introducing the character and numerical format specifiers to the student & college code we started earlier, you can remove the println() statement now and we will work exclusively with printf() from now on:
String studentName = "Andrew"; System.out.printf("%s is a student at this College.%n", studentName);
First, let’s create some data for this student to work with:
- initial (‘A’)
- student registration number (20114)
- average mark so far in his course (87.56)
- mark from his most recent exam (86)
Looking that this data you should be immediately aware of which datatype each will need be, if not then take a moment and review the Datatypes and Variables lesson. It is very important to remember all the theory you have learnt and how to apply it in practice as we move forward throughout this and all your courses.
So, let’s get coding by adding to our earlier code:
String studentName = "Andrew"; System.out.printf("%s is a student at this College.%n", studentName); char studentClassification= 'F'; int studentNumber = 20114; double averageMark = 87.56; int thisExamMark = 86;
Now, let’s use the printf() method to display the data with a little formatting by displaying the original sentence and then the additional data in a table format using Sub-Specifiers.
Sub-Specifiers
A format specifier can contain sub-specifiers that provide additional formatting options. The sub-specifier options vary with format specifiers, for example a floating-point sub-specifier has a precision option to determine how many digits follow the decimal point, whereas an integer sub-specifier does not since integers do not have decimal places. Let’s take a look at the three most common sub-specifiers and their options:
General syntax:
%(flags)(width)(.precision)f
- flags
- – : left justify output
- + : print a + sign for positive values
- 0 : pads the output with 0’s where the value has fewer characters than the width
- Space : prints a preceding space for positive values
- width
- number of characters to be printed
- .precision
- number of digits to follow the decimal point
General Syntax:
%(flags)(width)d
- flags
- – : left justify output
- + : print a + sign for positive values
- 0 : pads the output with 0’s where the value has fewer characters than the width
- Space : prints a preceding space for positive values
- width
- number of characters to be printed
General Syntax:
%(flags)(width)(.precision)s
- flags
- – : left justify output
- width
- number of characters to be printed. If the string has more characters than the width it will not be truncated. If the string has fewer character than the width, the width will be filled with spaces
- .precision
- Maximum number of characters to be printed. If the string has more characters than the width it will be truncated
Using sub-specifiers
Let’s start by building an output that will display our students information in an ordered way with headings, spacing and the data formatted in a way that makes sense and looks good. We could start with the headings: Number, Name, Type, Mark and Average. And we could include an additional line of dashes that will separate the headings from the data:
//Code previously written String studentName = "Andrew"; char studentClassification= 'F'; int studentNumber = 20114; double averageMark = 87.56; int thisExamMark = 86; //New code: System.out.printf("Number Name Type Mark Average%n"); System.out.printf("-----------------------------------------%n");
The output should be:
run: Number Name Type Mark Average ----------------------------------------- BUILD SUCCESSFUL (total time: 0 seconds)
This is looking pretty neat, we have inserted spaces between each heading and created enough dashes to match the number of characters (including spaces) in the heading. Next, let’s add the student data. This will be another printf() method and will display the values of studentName, studentClassification, studentNumber, averageMark and thisExamMark, using appropriate format specifiers. Take a moment to consider which format specifier you might use for each variable.
- studentName: this is a string so the format modifier will be %s
- studentClassification: this is a single character char type, and the format modifier will be %c
- studentNumber: this is a number of type int, this fits into the decimal (integer) category and will use the %d format modifier
- averageMark: this is a floating point number of type double and will use the %f format modifier
- thisExamMark: similar to studentNumber, this is a number of type int and will use the %d format modifier
Now that we have worked out the appropriate format specifiers for our variables, let’s write the code that will display them in the right order:
char studentType = 'F'; int studentNumber = 20114; double averageMark = 87.56; int thisExamMark = 86; System.out.printf("Number Name Type Mark Average%n"); System.out.printf("---------------------------------------%n"); System.out.printf("%d%s%c%d%f%n", studentNumber, studentName, studentType, thisExamMark, averageMark);
The output should be:
run: Number Name Type Mark Average ----------------------------------------- 20114AndrewF8687.560000 BUILD SUCCESSFUL (total time: 0 seconds)
You should have noticed that the data does not line up with the headings. One way to fix that would be to insert spaces between the data, for example:
System.out.printf(“%d %s %c %d %f%n“, studentNumber, studentName, studentType, thisExamMark, averageMark);
That would work for this data. But what if our data changes, or if we want to list more than one student? It would be impossible to use hard coded spaces to keep our data lined up. We have some options, we could use the tab format specifier, \t. This would insert a tab space. Let’s try that:
System.out.printf("Number Name Type Mark Average%n"); System.out.printf("---------------------------------------%n"); System.out.printf("%d\t%s\t%c\t%d\t%f%n", studentNumber, studentName, studentType, thisExamMark, averageMark);
The output should look like:
run: Number Name Type Mark Average ---------------------------------------- 20114 Andrew F 86 87.560000 BUILD SUCCESSFUL (total time: 0 seconds)
That’s pretty neat, the headings and data are lined up and the code is simpler, more responsive to varying data that doesn’t rely on hard coded spaces. However, there is one thing that stands out, the average mark have a lot of extra zeros. Wouldn’t be great if we could limit the number of digits that followed the decimal point? This is where sub-specifiers might be useful.
We know that sub-specifiers can provide additional formatting options format specifiers. The format specifier for the averageMark data is %f, the floating-point format specifier. As a reminder, the general syntax for the floating-point sub-specifier is:
%(flags)(width)(.precision)specifier
As this suggests, sub-specifiers are used within a format specifier and gives us options for flags (justification, 0 padding, etc.), width (the number of characters to be printed), and .precision (the number of digits to follow the decimal point) in that order. These options are optional, meaning we can choose to use as many as we need. Right now we just want to use the .precision (the dot is important) to limit the number of characters after the decimal point to two, so the .precision will be written as .2 and this will be located within the format specifier %f. So, that floating-point format modifier that looks like %f now will need to look like:
%.2f
Let’s modify our code to see how this works:
System.out.printf("Number Name Type Mark Average%n"); System.out.printf("---------------------------------------%n"); System.out.printf("%d\t%s\t%c\t%d\t%.2f %n", studentNumber, studentName, studentType, thisExamMark, averageMark);
The output should look like:
run: Number Name Type Mark Average ---------------------------------------- 20114 Andrew F 86 87.56 BUILD SUCCESSFUL (total time: 0 seconds)
This is looking much better. In addition to .precision, the floating-point sub-specifier has options for width, in fact all sub-specifiers have this option so let’s define a width for each piece of data. We do this by inserting the number of characters we would like each individual piece of data to occupy, observing the sequence of %(flags)(width)(.precision)specifier – can you see that the width must be positioned to the left of the .precision. So for averageMark let’s say we want a width of 7 characters, the format specifier will now look something like:
%7.2f
Can you see how this is building up? As long as we observe the sequence of options and we use the correct syntax (.number for precision, number for the width) Java will understand what we want it to do. Let’s use the width option and rewrite your code so that it does not use the tab format specifier but instead uses the width sub-specifier instead:
System.out.printf("Number Name Type Mark Average%n"); System.out.printf("---------------------------------------%n"); System.out.printf("%8d%8s%8c%8d%8.2f%n", studentNumber, studentName, studentType, thisExamMark, averageMark);
The output should look like:
run: Number Name Type Mark Average --------------------------------------- 20114 Andrew F 86 87.56 BUILD SUCCESSFUL (total time: 0 seconds)
Now, it looks like the data and headings are not in alignment but trust me, this is an improvement. We have reserved 8 characters for each piece of data which matches the number of characters that each heading occupies. The alignment issue is a reflection of the individual alignment of each piece of data, by default it will be right aligned but we can override that with the sub-specifier. Here is a reminder of the floating-point sub-specifier option:
%(flags)(width)(.precision)f
For a floating-point number, the flags give us options like left justify the output (-), print a + sign for positive values (+), pad the output with 0’s where the value has fewer characters than the width (0), prints a preceding space for positive values (Space). Care needs to be taken when using flags – for example, a string sub-specifier only had one flag option (the left justify option), and any attempt to use anything else will throw an exception.
Now that we know how to left align our data let’s update the code with:
System.out.printf(“%–8d%–8s%–8c%–8d%–8.2f%n”, studentNumber, studentName, studentType, thisExamMark, averageMark);
The output should be:
run: Number Name Type Mark Average --------------------------------------- 20114 Andrew F 86 87.56 BUILD SUCCESSFUL (total time: 0 seconds)
If you want to display additional characters you can include them in the format section, make sure they are within the double quotes but outside any format specifier. For example, let’s add a # symbol to the studentNumber data:
System.out.printf(“#%–7d%-8s%-8c%-8d%-8.2f%n“, studentNumber, studentName, studentType, thisExamMark, averageMark);
Notice that the # symbol is within the double quotes but outside the format specifier. Also, note that the width of the format specifier was reduced by 1 to allow for the inclusion of the # symbol.
What you have learnt
In this lesson you have learnt how to use the printf() method, and how to fine tune the format and display of data using format specifiers and sub-specifiers.