精彩文章免费看

带逗号的数值字符串转BigDecimal

项目需要将带有逗号的数值字符串转换为Money类型的数据,Money类型需要通过BigDecimal类型来构造。于是需要先将字符串转为BigDecimal类型。
字符串数据是带有逗号的,例:"25,000"。需要对字符串进行处理才能转换为BigDecimal。

version1: 先用replace将字符串里的逗号去掉,再构造BigDecimal

    public BigDecimal createBigDecimalFromString(String str) {
        try {
            String replaced = str.replace(",", "");
            BigDecimal result = new BigDecimal(replaced);
            return result;
        } catch (NumberFormatException e) {
            return null;

但是,用replace方法修改字符串并不优雅,有没有更好的方法可以直接读取该格式的字符串呢。

发现有DecimalFormat类,可以用它来直接读取带有逗号的数值字符串。

version2:用NumberFormat读取字符串,得到Number类型对象。用DecimalFormat将Number转为String类型,再通过String构造BigDecimal(注:new DecimalFormat("#")是因为该案例里的数值都是整数。"#"表示取整数,"#.##"表示取整数及小数点后2位)

    private BigDecimal createBigDecimalUseDecimalFormat(String str) {
        DecimalFormat format = new DecimalFormat();
        // or
        // NumberFormat format = NumberFormat.getInstance();
        // NumberFormat format = new DecimalFormat();
        try {
            Number resultNumber = format.parse(str);
            DecimalFormat dfFormat = new DecimalFormat("#");
            String numberStr = dfFormat.format(resultNumber);
            BigDecimal result = new BigDecimal(numberStr);
            return result;
        } catch (ParseException e) {
            return null;

该版本有一个很大的问题,就是Number转为String类型时可能出现精度损失。例如处理字符串"1,000,000,222,111,444,333,555",将其转换为Number类型会成为"1.0000002221114444E21",再转为String变成"1000000222111444400000"。精度损失是绝对不允许的。似乎Number类型没有将Number转为BigDecimal的方法。怎么办呢。

查询Stack Overflow,发现在使用DecimalFormat读取数值字符串之前,可以调用DecimalFormat的setParseBigDecima,它可以设置读取字符串时是否可以parse为BigDecimal,在读取数值字符串时,通过强制类型转换得到BigDecimal。

version3:

    private BigDecimal readStrAsBigDecimal(String str) {
        DecimalFormat format = new DecimalFormat();
        format.setParseBigDecimal(true);
        try {
            BigDecimal parse = (BigDecimal) format.parse(str);
            return parse;
        } catch (ParseException e) {
            e.printStackTrace();
            return null;

经过测试,该方法不会再有精度损失了。但是在测试时发现,如果在数值字符串的中间或者末尾加入字母,parse时也不会抛出异常。例如:"1,000,000,222,111,444,333,555aaa"转换后会得到结果"1000000222111444333555";"1,000aaa,000,222,111,444,333,555"转换后得到结果"1000"。这也不是我们想要的结果,因为我们的数据本身存在一些带有字母异常数据,而我们不能将异常数据当做正常数据进行处理。

在读取字符串时使用的parse方法有一个带有两个入参的实现:public Number parse(String text, ParsePosition pos);pos会记录parse结果的最后一位在原字符串中的位置。如果字符串完全由数值和数值分隔符号组成的话,那parse结束后pos的位置应该等于该字符串的长度。例:"1,000"parse结束后,pos的index值应该为5,等于字符串长度;"1,000a,00"parse结束后,pos的index值为5,不等于字符串长度9。我们可以通过pos值来判断被处理的字符串是否是异常字符串。(parse(String text, ParsePosition pos)方法不会抛出任何异常)

version4:

    public BigDecimal readStrAsBigDecimalAndCheckFormat(String str) {
        DecimalFormat format = new DecimalFormat();
        format.setParseBigDecimal(true);
        ParsePosition position = new ParsePosition(0);
        BigDecimal parse = (BigDecimal) format.parse(str, position);
        if (str.length() == position.getIndex()) {
            return parse;
        return null;

使用该方法成功将原字符串转为了没有精度损失的BigDecimal类型,并且不会由异常源数据得到错误结果。

最后编辑于:2019-03-28 11:39