# 23 Dynamic Formatting

It is almost impossible to get this far in an introductory programming course without making extensive use of format specifiers (e.g., with the `printf()` method in the `PrintWriter` class or the `format()` method in the `String` class). However, most, if not all, of the format specifiers you have seen and/or used have probably been hard-coded. It turns out that there are many situations in which format specifiers must be created while a program is running.

# Motivation

If you want to print all of the elements of a non-negative `int[]` in a field of width 10, it’s easy to do so using the format specifier `"%10d"` as follows:

```        for (int i = 0; i < data.length; i++) {
System.out.printf("%10d\n", data[i]);
}
```

However, now suppose, instead, that you want the field to be as narrow as possible. Since you can’t know the value of the elements of the array when you are writing the program, you can’t hard-code the format specifier.

# Review

Fortunately, you already have some patterns that can help you solve this problem. First, from the discussion of accumulators in Chapter 13, you know that you can find the largest `int` in an `int[]` named `data` as follows:

```        max = -1;
for (int i = 0; i < data.length; i++) {
if (data[i] > max) max = data[i];
}
```

Second, from the discussion of digit counting in Chapter 11, you know that you can find the number of digits in an `int` value named `max` as follows:

```        width = (int) (Math.log10(max)) + 1;
```

So, all you need to complete the solution to the dynamic formatting problem is a format specifier.

Fortunately, the format specifier is a `String` object, and you can construct and manipulate `String` objects while a program is running. For example, returning to the situation in which you want to use a field of width `10`, you could use a `String` variable named `fs` for the format `String` as follows:

```        fs = "%10d\n";

for (int i = 0; i < data.length; i++) {
System.out.printf(fs, data[i]);
}
```

Now, all you need to do is replace the hard-coded `10` in `fs` with the value contained in a variable.

# The Pattern

In particular, what you need to do is use `String` concatenation (or a `StringBuilder` object) to construct the format `String`. Recall that a format specifier has the following syntax:

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

where:

• flags is one or more of: `-` to indicate left-justification, `+` to indicate required inclusion of the sign, `,` to include grouping separators, etc.
• width indicates the width of the field
• precision indicates the number of digits to the right of the decimal point for real numbers
• conversion is one of `b` for a `boolean`, `c` for a `char`, `d` for an integer, `f` for a real number, `s` for a `String`, etc.

and items in square brackets are optional.

So, assuming all of the variables have been declared, you can construct a format specifier at run-time as follows:

```        fs = "%";
if (flags != null) fs += flags;
if (width > 0)     fs += width;
if (precision > 0) fs += "." + precision;
fs += conversion;
```

# Examples

Suppose you want to illustrate the non-repeating nature of the digits of $\pi$ by printing a table in which the first line contains one digit to the right of the decimal point, the second contains two digits to the right of the decimal point, etc. To accomplish this you need to construct the format specifier inside of a loop, and print `Math.PI` using that format specifier at each iteration. This can be accomplished as follows:

```        for (int digits = 1; digits <= 10; digits++) {
fs = "%" + (digits + 2) + "." + digits + "f\n";
System.out.printf(fs, Math.PI);
}
```

Note that this example uses `digits + 2` to account for the leading `3.` in the output.

As another example, suppose you want to create a `String` called `result` from a `String` called `source`, and you want `result` to satisfy the following specifications:

1. It must have `width` characters in total; and
2. The characters in `source` must be centered within `result`.

You know from the discussion of centering in Chapter 21 that there must be `width - source.length()` spaces in `result` with “half” of them to the left of `source` and “half” of them to the right of `source`. This can be accomplished as follows:

```    public static String center(String source, int width) {
int      field, n, append;
String   fs, result;

// Calculate the number of spaces in the resulting String
n = width - source.length();
if (n <= 0) return source;

// Calculate the width of the field for source (it will be
// right-justified in this field)
field = (width + source.length()) / 2;

// Calculate the number of spaces to append to the right
append = width - field;

// Build the format specifier
fs = "%" + field + "s%" + append + "s";

result = String.format(fs, source, " ");
return result;
}
```

The `source` will be right justified in a field that is `(width/2 - source.length())/2` characters wide and it will be followed by a single space that will be right justified in a field that is as wide as is necessary to fill the field.