sourcecode

Safe String to BigDecimal 변환

copyscript 2022. 11. 16. 21:22
반응형

Safe String to BigDecimal 변환

문자열에서 BigDecimal 값을 읽으려고 합니다.예를 들어 "1,000,000.9999999999999999999999999"라는 문자열이 있는데 BigDecimal을 얻으려고 합니다.어떻게 하면 좋을까요?

우선 문자열 치환(쉼표 치환 등)을 사용하는 솔루션이 마음에 들지 않습니다.그 일을 해 줄 멋진 포메터가 있어야 할 것 같아요.

DecimalFormatter 클래스는 찾았는데, 2배의 정밀도로 작동하기 때문에 큰 정확도가 손실됩니다.

그럼 어떻게 해야 하죠?

Decimal Format에서 체크 아웃합니다.이 세터를 사용하면 Big Decimal이 반환됩니다.

String value = "1,000,000,000.999999999999999";
BigDecimal money = new BigDecimal(value.replaceAll(",", ""));
System.out.println(money);

다음을 증명하는 풀 코드NumberFormatException던집니다.

import java.math.BigDecimal;

public class Tester {
    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);
    }
}

산출량

1000000000.999999999999999

다음 샘플 코드는 정상적으로 동작합니다(로케일은 동적으로 취득할 필요가 있습니다).

import java.math.BigDecimal;
import java.text.NumberFormat;
import java.text.DecimalFormat;
import java.text.ParsePosition;
import java.util.Locale;

class TestBigDecimal {
    public static void main(String[] args) {

        String str = "0,00";
        Locale in_ID = new Locale("in","ID");
        //Locale in_ID = new Locale("en","US");

        DecimalFormat nf = (DecimalFormat)NumberFormat.getInstance(in_ID);
        nf.setParseBigDecimal(true);

        BigDecimal bd = (BigDecimal)nf.parse(str, new ParsePosition(0));

        System.out.println("bd value : " + bd);
    }
}

코드는 더 깨끗할 수 있지만, 이것은 다른 지역에도 효과가 있는 것 같습니다.

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);
    }
}

방법은 다음과 같습니다.

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

물론, 만약 이것이 생산 코드로 진행된다면, 그렇게 간단하지 않을 것입니다.

String에서 콤마를 삭제하기만 하면 문제가 없습니다.

resultString = subjectString.replaceAll("[^.\\d]", "");

문자열에서 숫자와 점을 제외한 모든 문자가 삭제됩니다.

로케일을 인식하려면 에서 를 사용하는 것이 좋습니다.Java는 모르겠지만 다음과 같습니다.

sep = getDecimalSeparator()
resultString = subjectString.replaceAll("[^"+sep+"\\d]", "");

로케일을 알지 못하고 로케일에 의존하지 않고 String을 Big Decimal로 변환하는 솔루션이 필요했습니다.저는 이 문제에 대한 표준 해결책을 찾을 수 없어서 저만의 도우미 방법을 썼습니다.다른 사람에게도 도움이 될 수 있습니다.

업데이트: 경고!이 도우미 방식은 10진수에만 적용되므로 항상 소수점이 있는 숫자!그렇지 않으면 도우미 메서드가 1000 ~99999(플러스/마이너스)의 숫자에 잘못된 결과를 제공할 수 있습니다.bezmax의 훌륭한 인풋 덕분입니다!

static final String EMPTY = "";
static final String POINT = '.';
static final String COMMA = ',';
static final String POINT_AS_STRING = ".";
static final String COMMA_AS_STRING = ",";

/**
     * Converts a String to a BigDecimal.
     *     if there is more than 1 '.', the points are interpreted as thousand-separator and will be removed for conversion
     *     if there is more than 1 ',', the commas are interpreted as thousand-separator and will be removed for conversion
     *  the last '.' or ',' will be interpreted as the separator for the decimal places
     *  () or - in front or in the end will be interpreted as negative number
     *
     * @param value
     * @return The BigDecimal expression of the given string
     */
    public static BigDecimal toBigDecimal(final String value) {
        if (value != null){
            boolean negativeNumber = false;

            if (value.containts("(") && value.contains(")"))
               negativeNumber = true;
            if (value.endsWith("-") || value.startsWith("-"))
               negativeNumber = true;

            String parsedValue = value.replaceAll("[^0-9\\,\\.]", EMPTY);

            if (negativeNumber)
               parsedValue = "-" + parsedValue;

            int lastPointPosition = parsedValue.lastIndexOf(POINT);
            int lastCommaPosition = parsedValue.lastIndexOf(COMMA);

            //handle '1423' case, just a simple number
            if (lastPointPosition == -1 && lastCommaPosition == -1)
                return new BigDecimal(parsedValue);
            //handle '45.3' and '4.550.000' case, only points are in the given String
            if (lastPointPosition > -1 && lastCommaPosition == -1){
                int firstPointPosition = parsedValue.indexOf(POINT);
                if (firstPointPosition != lastPointPosition)
                    return new BigDecimal(parsedValue.replace(POINT_AS_STRING, EMPTY));
                else
                    return new BigDecimal(parsedValue);
            }
            //handle '45,3' and '4,550,000' case, only commas are in the given String
            if (lastPointPosition == -1 && lastCommaPosition > -1){
                int firstCommaPosition = parsedValue.indexOf(COMMA);
                if (firstCommaPosition != lastCommaPosition)
                    return new BigDecimal(parsedValue.replace(COMMA_AS_STRING, EMPTY));
                else
                    return new BigDecimal(parsedValue.replace(COMMA, POINT));
            }
            //handle '2.345,04' case, points are in front of commas
            if (lastPointPosition < lastCommaPosition){
                parsedValue = parsedValue.replace(POINT_AS_STRING, EMPTY);
                return new BigDecimal(parsedValue.replace(COMMA, POINT));
            }
            //handle '2,345.04' case, commas are in front of points
            if (lastCommaPosition < lastPointPosition){
                parsedValue = parsedValue.replace(COMMA_AS_STRING, EMPTY);
                return new BigDecimal(parsedValue);
            }
            throw new NumberFormatException("Unexpected number format. Cannot convert '" + value + "' to BigDecimal.");
        }
        return null;
    }

물론 그 방법을 테스트했습니다.

@Test(dataProvider = "testBigDecimals")
    public void toBigDecimal_defaultLocaleTest(String stringValue, BigDecimal bigDecimalValue){
        BigDecimal convertedBigDecimal = DecimalHelper.toBigDecimal(stringValue);
        Assert.assertEquals(convertedBigDecimal, bigDecimalValue);
    }
    @DataProvider(name = "testBigDecimals")
    public static Object[][] bigDecimalConvertionTestValues() {
        return new Object[][] {
                {"5", new BigDecimal(5)},
                {"5,3", new BigDecimal("5.3")},
                {"5.3", new BigDecimal("5.3")},
                {"5.000,3", new BigDecimal("5000.3")},
                {"5.000.000,3", new BigDecimal("5000000.3")},
                {"5.000.000", new BigDecimal("5000000")},
                {"5,000.3", new BigDecimal("5000.3")},
                {"5,000,000.3", new BigDecimal("5000000.3")},
                {"5,000,000", new BigDecimal("5000000")},
                {"+5", new BigDecimal("5")},
                {"+5,3", new BigDecimal("5.3")},
                {"+5.3", new BigDecimal("5.3")},
                {"+5.000,3", new BigDecimal("5000.3")},
                {"+5.000.000,3", new BigDecimal("5000000.3")},
                {"+5.000.000", new BigDecimal("5000000")},
                {"+5,000.3", new BigDecimal("5000.3")},
                {"+5,000,000.3", new BigDecimal("5000000.3")},
                {"+5,000,000", new BigDecimal("5000000")},
                {"-5", new BigDecimal("-5")},
                {"-5,3", new BigDecimal("-5.3")},
                {"-5.3", new BigDecimal("-5.3")},
                {"-5.000,3", new BigDecimal("-5000.3")},
                {"-5.000.000,3", new BigDecimal("-5000000.3")},
                {"-5.000.000", new BigDecimal("-5000000")},
                {"-5,000.3", new BigDecimal("-5000.3")},
                {"-5,000,000.3", new BigDecimal("-5000000.3")},
                {"-5,000,000", new BigDecimal("-5000000")},
                {null, null}
        };
    }

오래된 토픽이지만 가장 쉬운 것은 createBigDecimal(String 값) 메서드를 가진 Apache Commons NumberUtils를 사용하는 것입니다.

내 생각에 그것은 지역적인 것을 고려하지 않으면 오히려 쓸모없을 것이다.

이거 한번 써보세요.

BigDecimal bd ;
String value = "2000.00";

bd = new BigDecimal(value);
BigDecimal currency = bd;

언급URL : https://stackoverflow.com/questions/3752578/safe-string-to-bigdecimal-conversion

반응형