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
'sourcecode' 카테고리의 다른 글
대조란 무슨 뜻입니까? (0) | 2022.11.17 |
---|---|
MySQL에서 두 날짜의 일수를 구하는 방법은 무엇입니까? (0) | 2022.11.16 |
Python 문자열에서 특정 문자 제거 (0) | 2022.11.16 |
마리아DB / Python의 이모티콘 문제 (0) | 2022.11.16 |
PDO 사용 여부/필수:null 값을 바인딩할 때 PARAM_NULL? (0) | 2022.11.16 |