[java] Printing with "\t" (tabs) does not result in aligned columns

I have a very weird problem. After writing this:

for (File f : currentFile.listFiles()) {            
    if  (f.isDirectory()){
        System.out.println(f.getName()+"\t"+"Dir\t"+Command.getpremission(f)+"\t"+f.getTotalSpace());
    }
    else{
        System.out.println(f.getName()+"\t"+"File\t"+Command.getpremission(f)+"\t"+f.getTotalSpace());
    }

I see this printed:

see.txt File    rw  267642728448
see1.txt    File    rw  267642728456
see2.txt    File    rw  267642728448

Why is there a problem with the tabs?

This question is related to java tabs text-formatting

The answer is


You can use this example to handle your problem:

System.out.printf( "%-15s %15s %n", "name", "lastname");
System.out.printf( "%-15s %15s %n", "Bill", "Smith");

You can play with the "%" until you find the right alignment to satisfy your needs


The problem is the length of the filenames. The first filename is only 7 chars long, so the tab occurs at char 8 (doing a tab after every 4 characters). However the next filenames are 8 chars long, so the next tab won't be until char 12. And if you had filenames longer than 11 chars, you'd run into the same problem again.


In continuation of the comments by Péter and duncan, I normally use a quick padding method, something like -

public String rpad(String inStr, int finalLength)
{
    return (inStr + "                          " // typically a sufficient length spaces string.
        ).substring(0, finalLength);
}

similarly you can have a lpad() as well


The length of the text that you are providing in each line is different, this is the problem, so if the second word is too long (see2.txt is long 8 char which corresponds to a single tab lenght) it prints out a tab which goes to the next tabulation point. One way to solve it is to programmatically add a pad to the f.getName() text so each text generated: see.txt or see2.txt has the same lenght (for example see.txt_ and see2.txt) so each tab automatically goes to the same tabulation point.

If you are developing with JDK 1.5 you can solve this using java.util.Formatter:

String format = "%-20s %5d\n";
System.out.format(format, "test", 1);
System.out.format(format, "test2", 20);
System.out.format(format, "test3", 5000);

this example will give you this print:

test                     1
test2                   20
test3                 5000

Building on this question, I use the following code to indent my messages:

String prefix1 = "short text:";
String prefix2 = "looooooooooooooong text:";
String msg = "indented";
/*
* The second string begins after 40 characters. The dash means that the
* first string is left-justified.
*/
String format = "%-40s%s%n";
System.out.printf(format, prefix1, msg);
System.out.printf(format, prefix2, msg);

This is the output:

short text:                             indented
looooooooooooooong text:                indented

This is documented in section "Flag characters" in man 3 printf.


As mentioned by other folks, the variable length of the string is the issue.

Rather than reinventing the wheel, Apache Commons has a nice, clean solution for this in StringUtils.

StringUtils.rightPad("String to extend",100); //100 is the length you want to pad out to.

You can also pad a string to the required length using Guava's Strings.padEnd(String input, int minLength, char padding)


The "problem" with the tabs is that they indent the text to fixed tab positions, typically multiples of 4 or 8 characters (depending on the console or editor displaying them). Your first filename is 7 chars, so the next tab stop after its end is at position 8. Your subsequent filenames however are 8 chars long, so the next tab stop is at position 12.

If you want to ensure that columns get nicely indented at the same position, you need to take into account the actual length of previous columns, and either modify the number of following tabs, or pad with the required number of spaces instead. The latter can be achieved using e.g. System.out.printf with an appropriate format specification (e.g. "%1$13s" specifies a minimum width of 13 characters for displaying the first argument as a string).