21

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?

skaffman
  • 398,947
  • 96
  • 818
  • 769
sarasota
  • 295
  • 2
  • 4
  • 8

6 Answers6

21

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
Neeme Praks
  • 8,956
  • 5
  • 47
  • 47
RealHowTo
  • 34,977
  • 11
  • 70
  • 85
  • One big problem is that string.format is very slow and inefficient, especially if I need to make tens of thousand of that per second. – mk7 Jul 29 '23 at 21:12
8

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

Community
  • 1
  • 1
polygenelubricants
  • 376,812
  • 128
  • 561
  • 623
1

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));
        }
    }
}
Oliver Coleman
  • 734
  • 7
  • 13
1

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.

Benoit Courtine
  • 7,014
  • 31
  • 42
  • 3
    That causes extra leading zeros to get printed in the front like : 099.0 but what I want is [space]99.0 – sarasota Jul 26 '10 at 22:57
0

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();
    }
}
Benoit Courtine
  • 7,014
  • 31
  • 42
  • Maybe this was intended for versions of Java older than 6, but it doesn't work for Java 6 or 7. I've added another answer using a similar approach that is more flexible and is tested to work with Java 6+. – Oliver Coleman May 29 '14 at 23:28
-1

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.

user unknown
  • 35,537
  • 11
  • 75
  • 121