This commit is contained in:
Looly 2024-01-23 16:34:29 +08:00
parent f69a9db7b3
commit b7508cc0e9
11 changed files with 161 additions and 79 deletions

View File

@ -19,6 +19,8 @@ import org.dromara.hutool.core.convert.impl.CollectionConverter;
import org.dromara.hutool.core.convert.impl.EnumConverter;
import org.dromara.hutool.core.convert.impl.MapConverter;
import org.dromara.hutool.core.lang.Assert;
import org.dromara.hutool.core.math.NumberChineseFormatter;
import org.dromara.hutool.core.math.NumberWordFormatter;
import org.dromara.hutool.core.reflect.TypeReference;
import org.dromara.hutool.core.text.StrUtil;
import org.dromara.hutool.core.text.UnicodeUtil;

View File

@ -66,7 +66,11 @@ public class NumberConverter extends AbstractConverter {
@Override
protected String convertToStr(final Object value) {
final String result = StrUtil.trim(super.convertToStr(value));
if (null != result && result.length() > 1) {
if(StrUtil.isEmpty(result)){
throw new ConvertException("Can not convert empty value to Number!");
}
if (result.length() > 1) {
// 非单个字符才判断末尾的标识符
final char c = Character.toUpperCase(result.charAt(result.length() - 1));
if (c == 'D' || c == 'L' || c == 'F') {
@ -75,9 +79,6 @@ public class NumberConverter extends AbstractConverter {
}
}
if(StrUtil.isEmpty(result)){
throw new ConvertException("Can not convert empty value to Number!");
}
return result;
}

View File

@ -13,7 +13,7 @@
package org.dromara.hutool.core.date;
import org.dromara.hutool.core.comparator.CompareUtil;
import org.dromara.hutool.core.convert.NumberChineseFormatter;
import org.dromara.hutool.core.math.NumberChineseFormatter;
import org.dromara.hutool.core.date.format.GlobalCustomFormat;
import org.dromara.hutool.core.date.format.parser.DateParser;
import org.dromara.hutool.core.date.format.parser.FastDateParser;

View File

@ -12,7 +12,7 @@
package org.dromara.hutool.core.date.chinese;
import org.dromara.hutool.core.convert.NumberChineseFormatter;
import org.dromara.hutool.core.math.NumberChineseFormatter;
import org.dromara.hutool.core.date.CalendarUtil;
import org.dromara.hutool.core.date.DateTime;
import org.dromara.hutool.core.date.DateUtil;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2023 looly(loolly@aliyun.com)
* Copyright (c) 2023-2024. looly(loolly@aliyun.com)
* Hutool is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
@ -10,10 +10,9 @@
* See the Mulan PSL v2 for more details.
*/
package org.dromara.hutool.core.convert;
package org.dromara.hutool.core.math;
import org.dromara.hutool.core.lang.Assert;
import org.dromara.hutool.core.math.NumberUtil;
import org.dromara.hutool.core.text.StrUtil;
import org.dromara.hutool.core.array.ArrayUtil;

View File

@ -0,0 +1,109 @@
/*
* Copyright (c) 2024. looly(loolly@aliyun.com)
* Hutool is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* https://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
package org.dromara.hutool.core.math;
import org.dromara.hutool.core.text.StrUtil;
/**
* 数字和罗马数字转换
*
* @author dazer
* @since 6.0.0
*/
public class NumberRomanFormatter {
/**
* 整数转罗马数字<br>
* 限制[1,3999]的正整数
* <ul>
* <li>I 1</li>
* <li>V 5</li>
* <li>X 10</li>
* <li>L 50</li>
* <li>C 100</li>
* <li>D 500</li>
* <li>M 1000</li>
* </ul>
*
* @param num [1,3999]的正整数
* @return 罗马数字
* @author dazer
* @since 6.0.0
*/
public static String intToRoman(final int num) {
if (num > 3999 || num < 1) {
return StrUtil.EMPTY;
}
final String[] thousands = {"", "M", "MM", "MMM"};
final String[] hundreds = {"", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"};
final String[] tens = {"", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"};
final String[] ones = {"", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"};
return thousands[num / 1000] +
hundreds[(num % 1000) / 100] +
tens[(num % 100) / 10] +
ones[num % 10];
}
/**
* 罗马数字转整数<br>
*
* @param roman 罗马字符
* @return 整数
* @throws IllegalArgumentException 如果传入非罗马字符串抛出异常
* @author dazer
* @since 6.0.0
*/
public static int romanToInt(final String roman) {
int result = 0;
int prevValue = 0;
int currValue;
for (int i = roman.length() - 1; i >= 0; i--) {
final char c = roman.charAt(i);
switch (c) {
case 'I':
currValue = 1;
break;
case 'V':
currValue = 5;
break;
case 'X':
currValue = 10;
break;
case 'L':
currValue = 50;
break;
case 'C':
currValue = 100;
break;
case 'D':
currValue = 500;
break;
case 'M':
currValue = 1000;
break;
default:
throw new IllegalArgumentException("Invalid Roman character: " + c);
}
if (currValue < prevValue) {
result -= currValue;
} else {
result += currValue;
}
prevValue = currValue;
}
return result;
}
}

View File

@ -903,16 +903,16 @@ public class NumberUtil extends NumberValidator {
/**
* 数字转{@link BigDecimal}<br>
* null或"""NaN"或空白符转换为0
* null或""或空白符抛出{@link IllegalArgumentException}异常<br>
* "NaN"转为{@link BigDecimal#ZERO}
*
* @param numberStr 数字字符串
* @return {@link BigDecimal}
* @since 4.0.9
* @throws IllegalArgumentException null或"""NaN"或空白符抛出此异常
*/
public static BigDecimal toBigDecimal(final String numberStr) {
if (StrUtil.isBlank(numberStr)) {
return BigDecimal.ZERO;
}
public static BigDecimal toBigDecimal(final String numberStr) throws IllegalArgumentException{
// 统一规则不再转换带有歧义的null""和空格
Assert.notBlank(numberStr, "Number str must be not blank!");
// issue#3241优先调用构造解析
try{
@ -934,9 +934,8 @@ public class NumberUtil extends NumberValidator {
* @since 5.4.5
*/
public static BigInteger toBigInteger(final Number number) {
if (null == number) {
return BigInteger.ZERO;
}
// 统一规则不再转换带有歧义的null
Assert.notNull(number, "Number must be not null!");
if (number instanceof BigInteger) {
return (BigInteger) number;
@ -951,12 +950,21 @@ public class NumberUtil extends NumberValidator {
* 数字转{@link BigInteger}<br>
* null或""或空白符转换为0
*
* @param number 数字字符串
* @param numberStr 数字字符串
* @return {@link BigInteger}
* @since 5.4.5
*/
public static BigInteger toBigInteger(final String number) {
return StrUtil.isBlank(number) ? BigInteger.ZERO : new BigInteger(number);
public static BigInteger toBigInteger(final String numberStr) {
// 统一规则不再转换带有歧义的null""和空格
Assert.notBlank(numberStr, "Number str must be not blank!");
try{
return new BigInteger(numberStr);
} catch (final Exception ignore){
// 忽略解析错误
}
return parseBigInteger(numberStr);
}
/**
@ -1057,7 +1065,7 @@ public class NumberUtil extends NumberValidator {
* @return 0或非0值
*/
public static BigInteger nullToZero(final BigInteger number) {
return number == null ? BigInteger.ZERO : number;
return ObjUtil.defaultIfNull(number, BigInteger.ZERO);
}
/**
@ -1574,18 +1582,7 @@ public class NumberUtil extends NumberValidator {
* @author dazer
*/
public static String intToRoman(final int num) {
if (num > 3999 || num < 1 ){
return StrUtil.EMPTY;
}
final String[] thousands = {"", "M", "MM", "MMM"};
final String[] hundreds = {"", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"};
final String[] tens = {"", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"};
final String[] ones = {"", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"};
return thousands[num / 1000] +
hundreds[(num % 1000) / 100] +
tens[(num % 100) / 10] +
ones[num % 10];
return NumberRomanFormatter.intToRoman(num);
}
/**
@ -1597,45 +1594,6 @@ public class NumberUtil extends NumberValidator {
* @author dazer
*/
public static int romanToInt(final String roman) {
int result = 0;
int prevValue = 0;
int currValue;
for (int i = roman.length() - 1; i >= 0; i--) {
final char c = roman.charAt(i);
switch (c) {
case 'I':
currValue = 1;
break;
case 'V':
currValue = 5;
break;
case 'X':
currValue = 10;
break;
case 'L':
currValue = 50;
break;
case 'C':
currValue = 100;
break;
case 'D':
currValue = 500;
break;
case 'M':
currValue = 1000;
break;
default:
throw new IllegalArgumentException("Invalid Roman character: " + c);
}
if (currValue< prevValue) {
result -= currValue;
} else {
result += currValue;
}
prevValue = currValue;
}
return result;
return NumberRomanFormatter.romanToInt(roman);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2023 looly(loolly@aliyun.com)
* Copyright (c) 2023-2024. looly(loolly@aliyun.com)
* Hutool is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
@ -10,7 +10,7 @@
* See the Mulan PSL v2 for more details.
*/
package org.dromara.hutool.core.convert;
package org.dromara.hutool.core.math;
import org.dromara.hutool.core.math.NumberUtil;
import org.dromara.hutool.core.text.StrUtil;

View File

@ -10,8 +10,9 @@
* See the Mulan PSL v2 for more details.
*/
package org.dromara.hutool.core.convert;
package org.dromara.hutool.core.math;
import org.dromara.hutool.core.convert.Convert;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

View File

@ -13,7 +13,6 @@
package org.dromara.hutool.core.math;
import org.dromara.hutool.core.lang.Console;
import org.dromara.hutool.core.math.NumberUtil;
import org.dromara.hutool.core.text.StrUtil;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
@ -380,6 +379,18 @@ public class NumberUtilTest {
Assertions.assertEquals(new BigDecimal("9.0E+7"), NumberUtil.toBigDecimal("9.0E+7"));
}
@Test
void emptyToBigDecimalTest(){
Assertions.assertThrows(IllegalArgumentException.class,()->{
NumberUtil.toBigDecimal("");
});
}
@Test
void naNToBigDecimalTest(){
Assertions.assertEquals(BigDecimal.ZERO, NumberUtil.toBigDecimal("NaN"));
}
@Test
public void issue2878Test() throws ParseException {
// https://github.com/dromara/hutool/issues/2878

View File

@ -10,8 +10,9 @@
* See the Mulan PSL v2 for more details.
*/
package org.dromara.hutool.core.convert;
package org.dromara.hutool.core.math;
import org.dromara.hutool.core.math.NumberWordFormatter;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;