views:

118

answers:

5

I'm trying to read some BigDecimal values from the string. Let's say I have this String: "1,000,000,000.999999999999999" and I want to get a BigDecimal out of it. What is the way to do it?

First of all, I don't like the solutions using string replaces (replacing commas etc.). I think there should be some neat formatter to do that job for me.

I've found a DecimalFormatter class, however as it operates through double - huge amounts of precision are lost.

So, how can I do it?

Thanks in advance.

+5  A: 

Check out this method in DecimalFormat. With this setter, the parse method will return a BigDecimal for you.

Jeroen Rosenberg
Constructor in the BigDecimal class does not support custom formats. The example I gave in the question uses the custom format (commas). You can't parse that to BigDecimal using it's constructor.
Max
If you're going to **completely** change your answer, I'd suggest mentioning it in the answer. Else it looks very odd when people have pointed out why your original answer didn't make any sense. :-)
T.J. Crowder
Yeah, did not notice that method. Thanks, that seems to be the best way to do it. Gonna test it now and if it works properly - accept the answer.
Max
I repaired the links and replaces 1.5 versions with 1.6 ones. +1, however
seanizer
+2  A: 

Here is how I would do it:

public String cleanDecimalString(String input, boolean americanFormat) {
    if (americanFormat)
        return input.replaceAll(",", "");
    else
        return input.replaceAll(".", "");
}

Obviously, if this were going in production code, it wouldn't be that simple.

I see no issue with simply removing the commas from the String.

jjnguy
And if the string is not in American format? In Germany, it would be 1.000.000.000,999999999999999
Steve McLeod
@Steve, good call
jjnguy
So you use the locale to clean it of the correct things.
T.J. Crowder
Main problem here is that the numbers could be fetched from different sources each having it's own format. So the best way to do it in this situation would be defining some "number format" for each of the sources. I can't do that with simple replacings as one source can have "123 456 789" format and the other "123.456.789" one.
Max
I see you are silently redefining the term "one line method"... ;-)
Péter Török
@Steve: that's a pretty fundamental difference that calls for explicit handling, not a "regexp fits all" approach.
Michael Borgwardt
@Peter, yeah. Oversight on my part. That sentence has been removed though.
jjnguy
@Max: *"Main problem here is that the numbers could be fetched from different sources each having it's own format."* Which argues **for**, rather than against, your cleaning the input before handing off to code you don't control.
T.J. Crowder
If you're going to assume that you can have inputs from different locales, then they are going to have to be stamped with a locale somehow. I suppose you could say that if you see "1,234,567.890" you know it's American style because you can't have more than one decimal point. Similarly "1.234.567,890" must be German style. But what if you see "123,456"? There's no way to tell without someone telling you.
Jay
@jay, that is correct. You cannot know the locale for sure without someone telling you first.
jjnguy
A: 
String value = "1,000,000,000.999999999999999";
BigDecimal money = new BigDecimal(value.replaceAll(",", ""));
System.out.println(money);

Full code to prove that no NumberFormatException is thrown:

/**
 * 
 */

import java.math.BigDecimal;

/**
 * @author The Elite Gentleman
 *
 */
public class Tester {

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        String value = "1,000,000,000.999999999999999";
        BigDecimal money = new BigDecimal(value.replaceAll(",", ""));
        System.out.println(money);
    }

}

Output

1000000000.999999999999999

The Elite Gentleman
This throws java.lang.NumberFormatException
Steve McLeod
@Steve McLeod....nope, I just tested it.... my value is `1000000000.999999999999999`
The Elite Gentleman
Try with GERMAN locale, and 1.000.000.000,999999999999
TofuBeer
@#TofuBeer....the example was 1,000,000,000.999999999999999. If I had to do your locale, I would have to replace all dots to space....
The Elite Gentleman
+1  A: 
resultString = subjectString.replaceAll("[^.\\d]", "");

will remove all characters except digits and the dot from your string.

To make it locale-aware, you might want to use getDecimalSeparator() from java.text.DecimalFormatSymbols. I don't know Java, but it might look like this:

sep = getDecimalSeparator()
resultString = subjectString.replaceAll("[^"+sep+"\\d]", "");
Tim Pietzcker
Which will give unexpected results for non-American numbers - see the comments to Justin's answer.
Péter Török
I know, was just editing my answer when you commented :)
Tim Pietzcker
A: 

The code could be cleaner, but this seems to do the trick for different locales.

import java.math.BigDecimal;
import java.text.DecimalFormatSymbols;
import java.util.Locale;


public class Main
{
    public static void main(String[] args)
    {
        final BigDecimal numberA;
        final BigDecimal numberB;

        numberA = stringToBigDecimal("1,000,000,000.999999999999999", Locale.CANADA);
        numberB = stringToBigDecimal("1.000.000.000,999999999999999", Locale.GERMANY);
        System.out.println(numberA);
        System.out.println(numberB);
    }

    private static BigDecimal stringToBigDecimal(final String formattedString,
                                                 final Locale locale)
    {
        final DecimalFormatSymbols symbols;
        final char                 groupSeparatorChar;
        final String               groupSeparator;
        final char                 decimalSeparatorChar;
        final String               decimalSeparator;
        String                     fixedString;
        final BigDecimal           number;

        symbols              = new DecimalFormatSymbols(locale);
        groupSeparatorChar   = symbols.getGroupingSeparator();
        decimalSeparatorChar = symbols.getDecimalSeparator();

        if(groupSeparatorChar == '.')
        {
            groupSeparator = "\\" + groupSeparatorChar;
        }
        else
        {
            groupSeparator = Character.toString(groupSeparatorChar);
        }

        if(decimalSeparatorChar == '.')
        {
            decimalSeparator = "\\" + decimalSeparatorChar;
        }
        else
        {
            decimalSeparator = Character.toString(decimalSeparatorChar);
        }

        fixedString = formattedString.replaceAll(groupSeparator , "");
        fixedString = fixedString.replaceAll(decimalSeparator , ".");
        number      = new BigDecimal(fixedString);

        return (number);
    }
}
TofuBeer