Skip to content

Exercise 7 of 10 · Strings and formatting

The Scoreboard

What you will make

A game scoreboard where every name, score and bar lands in the same column, with each row built from one formatted piece of text rather than pasted-together pieces.

The one new idea: String.format drops values into fixed-width slots so columns line up

Receipts, timetables and printed reports are all text with values dropped into fixed-width columns. String.format is how Java writes that, and reaching for it instead of gluing pieces together with plus signs is the habit that keeps a row of numbers from drifting out of line the moment one of them gains an extra digit.

Go straight to the code ↓

Building text with a pattern

Every line you have printed so far was either fixed text or several pieces joined with +. A plus sign works, but it never gives any one piece a fixed width, so as soon as you care what a line looks like — not just what it says — gluing pieces together stops being enough.

String.format takes a pattern with slots marked by a percent sign, and a list of values to drop into those slots in order:

Java
String player = "Ana";
int score = 12;
System.out.println(String.format("%s scored %d", player, score));
Output
Ana scored 12

%s means "a piece of text goes here", %d means "a whole number goes here", and String.format builds one finished piece of text with both slots filled in. System.out.println then prints that one piece of text, exactly as it would print anything else.

Widths are what make a column

A fixed width is the whole trick behind a column, and you put the width number straight inside the slot:

  • %-7s gives the name 7 characters. The - pushes it to the left, padding with spaces on the right until it fills the width — the way a label reads more naturally.
  • %3d gives the score 3 characters, pushed to the right by default, padding on the left. That is why a two-digit score and a one-digit score both finish in the same column.

Put those together with an ordinary space and a plain %s for whatever should not be padded at all, and every row built from the same pattern lines up under the last, no matter how long any one value happens to be.

A worked example

A bird tally rather than a scoreboard, so the answer stays yours to write:

Java
public class Main {
    public static void main(String[] args) {
        String bird = "Owl";
        int seen = 3;
        System.out.println("[" + bird + "][" + seen + "]");
        System.out.println(String.format("[%-6s][%4d]", bird, seen));

        bird = "Heron";
        seen = 14;
        System.out.println(String.format("[%-6s][%4d]", bird, seen));
    }
}
Output
[Owl][3]
[Owl   ][   3]
[Heron ][  14]

The square brackets are there only to make the padding visible. The glued row hugs its values; the two below give every name six characters and every count four, so both closing brackets land in the same column, and so do both numbers.

Your turn

The editor holds a scoreboard with three players. Each gets a name, a score, and bar = "#".repeat(score), which makes one copy of # per point.

Press Run first. Ana and Ben come out as tidy rows; Cleo's row is ragged, because her line is still glued together with plus signs and nothing gives her values a fixed width:

Java
System.out.println(player + " " + score + " " + bar);

Rewrite that one line using String.format, matching the pattern used by the two rows above it, so her row lines up with them. The widths you need are already on screen, in the two finished rows.

Then set her score to 11 and run it again: the bar grows, the row stays in column. Put the 5 back afterwards.

If something goes wrong

The likeliest slip is a missing %. If part of the row prints as literal letters like -7s rather than padded text, a % has gone missing from the front of a format specifier — String.format only treats the characters after a % as an instruction.

If the row prints in the wrong place, count the widths against the two rows above rather than guessing; one number different is all it takes to drift a column.

If a row goes missing entirely and nothing after it prints either, String.format was probably given fewer values than its pattern has slots for. Unlike a lot of Java mistakes, this one is caught only when the line runs, not when the program compiles.

Nothing here can break. Change a score, press Run, and read your own board.

Write your code

Runs in your browser. Press Run (or Ctrl/Cmd+Enter) and the output is checked for you.

Ctrl/Cmd+Enter to run

Press Esc then Tab to move keyboard focus out of the code editor.

Ready
Output will appear here after you run your code.

The runtime is starting in the background. You can type now — it will be ready before you are.

The answer appears here once you have run your code at least once.

Things that often go wrong here

Leaving the row built with plus signs instead
javac does not stop you. + between text and a number quietly turns the number into text and joins it on the end, and with nothing giving any value a fixed width, each one takes only the room it needs — so Cleo's row starts one character later than Ana's and Ben's, because their scores are two digits and hers is one.
Forgetting the % in front of a format specifier
String.format("-7s%3d %s", player, score, bar) does not print -7s as harmless leading text — it fails while the line runs. With the leading % gone, %3d becomes the first specifier in the pattern, so it grabs the first value instead, which is player, a piece of text, not a number. Formatting text with %d is a runtime failure this playground cannot turn into a catchable exception: Cleo's row never appears, and nothing after that line runs either.
Mixing up the width numbers between the three slots
The columns line up only because %-7s, %3d and the two rows above all agree on the same widths. Give the score four characters instead of three on this one row and it alone drifts a column out of step with Ana's and Ben's, even though every row still prints without complaint.
Leaving out one of the three values passed to String.format
String.format checks how many values it was given only when the line actually runs, not when the program compiles — javac has no way to know from the pattern alone how many slots it needs. Passing too few turns into a plain runtime failure the moment this line executes, and this playground cannot turn it into a normal Java exception a catch block could handle, so the row simply never appears and nothing after it runs either.

Want a blank editor instead? Open the Java playground.