sarasota
sarasota

Reputation: 305

Formatting numbers with same amount of padding

I want to format 3 digit floats in Java so they line up vertically such that they look like:

123.45
 99
 23.2
 45

When I use DecimalFormat class, I get close, but I want to insert spaces when the item has 1 or 2 digits.

My code:

DecimalFormat formatter = new java.text.DecimalFormat("####.##");
float [] floats = [123.45, 99.0, 23.2, 45.0];

for(int i=0; i<floats.length; i++)
{
    float value = floats[i];

    println(formatter.format(value));
}

It produces:

123.45
99
23.2
45

How can I print it so that all but the first line is shifted over by 1 space?

Upvotes: 21

Views: 30756

Answers (6)

RealHowTo
RealHowTo

Reputation: 35372

Try with String.format() (JavaDoc):

public static void main(String args[]){
  String format = "%10.2f\n";  // width == 10 and 2 digits after the dot
  float [] floats = {123.45f, 99.0f, 23.2f, 45.0f};
  for(int i=0; i<floats.length; i++) {
      float value = floats[i];
      System.out.format(format, value);
}

and the output is :

123.45
 99.00
 23.20
 45.00

Upvotes: 22

Oliver Coleman
Oliver Coleman

Reputation: 794

In the same vein as Benoit's answer, here's a class extending DecimalFormat which ensures a specified minimum length by left-padding formatted numbers with spaces. It's tested (Java 6, 7), more general and provides a working example.

import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.FieldPosition;

public class PaddingDecimalFormat extends DecimalFormat {
    private int minimumLength;

    /**
     * Creates a PaddingDecimalFormat using the given pattern and minimum minimumLength and the symbols for the default locale.
     */
    public PaddingDecimalFormat(String pattern, int minLength) {
        super(pattern);
        minimumLength = minLength;
    }

    /**
     * Creates a PaddingDecimalFormat using the given pattern, symbols and minimum minimumLength.
     */
    public PaddingDecimalFormat(String pattern, DecimalFormatSymbols symbols, int minLength) {
        super(pattern, symbols);
        minimumLength = minLength;
    }

    @Override
    public StringBuffer format(double number, StringBuffer toAppendTo, FieldPosition pos) {
        int initLength = toAppendTo.length();
        super.format(number, toAppendTo, pos);
        return pad(toAppendTo, initLength);
    }

    @Override
    public StringBuffer format(long number, StringBuffer toAppendTo, FieldPosition pos) {
        int initLength = toAppendTo.length();
        super.format(number, toAppendTo, pos);
        return pad(toAppendTo, initLength);
    }

    private StringBuffer pad(StringBuffer toAppendTo, int initLength) {
        int numLength = toAppendTo.length() - initLength;
        int padLength = minimumLength - numLength;
        if (padLength > 0) {
            StringBuffer pad = new StringBuffer(padLength);
            for(int i = 0; i < padLength; i++) {
                pad.append(' ');
            }
            toAppendTo.insert(initLength, pad);
        }
        return toAppendTo;
    }

    public static void main(String[] args) {
        PaddingDecimalFormat intFormat = new PaddingDecimalFormat("#", 6);
        for (int i = 0; i < 20; i++) {
            System.out.println(intFormat.format(i) + intFormat.format(i*i*i));
        }
    }
}

Upvotes: 1

polygenelubricants
polygenelubricants

Reputation: 383716

This is trivial with a bit of regular expression string replacement.

formatter.format(f).replaceAll("\\G0", " ")

Here it is in context: (see also on ideone.com):

    DecimalFormat formatter = new java.text.DecimalFormat("0000.##");
    float[] floats = {
        123.45f,     //  123.45
         99.0f,      //   99
         23.2f,      //   23.2
         12.345f,    //   12.35
           .1234f,   //     .12
        010.001f,    //   10
    };

    for(float f : floats) {
        String s = formatter.format(f).replaceAll("\\G0", " ");
        System.out.println(s);
    }

This uses DecimalFormat to do most of the formatting (the zero padding, the optional #, etc) and then uses String.replaceAll(String regex, String replacement) to replace all leading zeroes to spaces.

The regex pattern is \G0. That is, 0 that is preceded by \G, which is the "end of previous match" anchor. The \G is also present at the beginning of the string, and this is what allows leading zeroes (and no other zeroes) to be matched and replaced with spaces.

References


On escape sequences

The reason why the pattern \G0 is written as "\\G0" as a Java string literal is because the backslash is an escape character. That is, "\\" is a string of length one, containing the backslash.

References

Related questions


Additional tips

Note that I've used the for-each loop, which results in a much simpler code, thus enhancing readability and minimizing chances of mistakes. I've also kept the floating point variables as float, using the f suffix to declare them as float literals (since they're double by default otherwise), but it needs to be said that generally you should prefer double to float.

See also

Upvotes: 8

user unknown
user unknown

Reputation: 36229

Imagine semicolon and System.out.; formatter like above in question.

printf ("   ".substring (("" + Math.round (f)).length ) + formatter.format (f))

I would prefer a log-10-Function, to calculate the size of 999 or 100 to 3, instead of the implicit toString-call, but I just find logarithm naturalis.

Math.round (987.65) gives 987.
("" + 987).length gives 3
"___".substring (3) is the empty string, for a short number (0-9) it will return two blanks.

Upvotes: -1

Benoit Courtine
Benoit Courtine

Reputation: 7064

A method that should answer your problem (I wrote it directly here and did not tested it so you could have some bugs to correct, but at least, you get the idea) :

public class PaddingDecimalFormat extends DecimalFormat {

    private int maxIntLength = 1;

    public PaddingDecimalFormat(String pattern) {
        super(pattern);
    }

    public void configure(Number[] numbers) {
        for(Number number : numbers) {
             maxIntLength = Math.max(maxIntLength, Integer.toString(number.intValue()).length()); 
        }
    }

    @Override
    public void format(Number number) {
        int padding = maxIntLength - Integer.toString(number.intValue()).length();

        StringBuilder sb = new StringBuilder();

        for(int i=0; i<padding; i++) {
            sb.append(' ');
        }

        sb.append(super.format(number));

        return sb.toString();
    }
}

Upvotes: 0

Benoit Courtine
Benoit Courtine

Reputation: 7064

Just change your first line, replacing '#' characters by '0'. It will solve your problem and produce formatted numbers with the same length, as explicated in the Java API. With that method, your lines will start and end with additional '0' numbers (099.00 for example) :

DecimalFormat formatter = new java.text.DecimalFormat("0000.00");

If you want a correct alignment without theses useless '0', you'll have to create your own formatting method : it doesn't exist in the native Java API.

Upvotes: 1

Related Questions