diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/convert/Convert.java b/hutool-core/src/main/java/org/dromara/hutool/core/convert/Convert.java
index 210689647..71671af03 100644
--- a/hutool-core/src/main/java/org/dromara/hutool/core/convert/Convert.java
+++ b/hutool-core/src/main/java/org/dromara/hutool/core/convert/Convert.java
@@ -1047,7 +1047,7 @@ public class Convert {
* @return 数字
* @since 5.6.0
*/
- public static int chineseToNumber(final String number){
+ public static BigDecimal chineseToNumber(final String number){
return ChineseNumberParser.parseFromChineseNumber(number);
}
diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/math/ChineseNumberParser.java b/hutool-core/src/main/java/org/dromara/hutool/core/math/ChineseNumberParser.java
index d9723b55f..e14c46fcf 100644
--- a/hutool-core/src/main/java/org/dromara/hutool/core/math/ChineseNumberParser.java
+++ b/hutool-core/src/main/java/org/dromara/hutool/core/math/ChineseNumberParser.java
@@ -13,6 +13,7 @@
package org.dromara.hutool.core.math;
import org.dromara.hutool.core.array.ArrayUtil;
+import org.dromara.hutool.core.lang.Assert;
import org.dromara.hutool.core.text.StrUtil;
import java.math.BigDecimal;
@@ -52,8 +53,8 @@ public class ChineseNumberParser {
* @return 数字
* @since 5.6.0
*/
- public static Number parseFromChinese(final String chinese) {
- if(StrUtil.containsAny(chinese, '元', '圆', '角', '分')){
+ public static BigDecimal parseFromChinese(final String chinese) {
+ if (StrUtil.containsAny(chinese, '元', '圆', '角', '分')) {
return parseFromChineseMoney(chinese);
}
@@ -61,74 +62,36 @@ public class ChineseNumberParser {
}
/**
- * 把中文转换为数字 如 二百二十 220
+ * 把中文转换为数字
*
* - 一百一十二 -》 112
* - 一千零一十二 -》 1012
+ * - 十二点二三 -》 12.23
+ * - 三点一四一五九二六五四 -》 3.141592654
*
*
* @param chinese 中文字符
* @return 数字
* @since 5.6.0
*/
- public static int parseFromChineseNumber(final String chinese) {
- final int length = chinese.length();
- int result = 0;
+ public static BigDecimal parseFromChineseNumber(final String chinese) {
+ Assert.notBlank(chinese, "Chinese number is blank!");
+ final int dotIndex = chinese.indexOf('点');
- // 节总和
- int section = 0;
- int number = 0;
- ChineseUnit unit = null;
- char c;
- for (int i = 0; i < length; i++) {
- c = chinese.charAt(i);
- final int num = chineseToNumber(c);
- if (num >= 0) {
- if (num == 0) {
- // 遇到零时节结束,权位失效,比如两万二零一十
- if (number > 0 && null != unit) {
- section += number * (unit.value / 10);
- }
- unit = null;
- } else if (number > 0) {
- // 多个数字同时出现,报错
- throw new IllegalArgumentException(StrUtil.format("Bad number '{}{}' at: {}", chinese.charAt(i - 1), c, i));
- }
- // 普通数字
- number = num;
- } else {
- unit = chineseToUnit(c);
- if (null == unit) {
- // 出现非法字符
- throw new IllegalArgumentException(StrUtil.format("Unknown unit '{}' at: {}", c, i));
- }
+ // 整数部分
+ BigDecimal result = NumberUtil.toBigDecimal(parseLongFromChineseNumber(chinese, dotIndex > 0 ? dotIndex : chinese.length()));
- //单位
- if (unit.secUnit) {
- // 节单位,按照节求和
- section = (section + number) * unit.value;
- result += section;
- section = 0;
- } else {
- // 非节单位,和单位前的单数字组合为值
- int unitNumber = number;
- if (0 == number && 0 == i) {
- // issue#1726,对于单位开头的数组,默认赋予1
- // 十二 -> 一十二
- // 百二 -> 一百二
- unitNumber = 1;
- }
- section += (unitNumber * unit.value);
- }
- number = 0;
+ // 小数部分
+ if (dotIndex > 0) {
+ final int length = chinese.length();
+ for (int i = dotIndex + 1; i < length; i++) {
+ // 保留位数取决于实际数字的位数
+ // result + (numberChar / 10^(i-dotIndex))
+ result = result.add(NumberUtil.div(chineseToNumber(chinese.charAt(i)), BigDecimal.TEN.pow(i-dotIndex), (length - dotIndex + 1)));
}
}
- if (number > 0 && null != unit) {
- number = number * (unit.value / 10);
- }
-
- return result + section + number;
+ return result.stripTrailingZeros();
}
/**
@@ -192,15 +155,15 @@ public class ChineseNumberParser {
}
//元、角、分
- int y = 0, j = 0, f = 0;
+ long y = 0, j = 0, f = 0;
if (StrUtil.isNotBlank(yStr)) {
- y = parseFromChineseNumber(yStr);
+ y = parseLongFromChineseNumber(yStr, yStr.length());
}
if (StrUtil.isNotBlank(jStr)) {
- j = parseFromChineseNumber(jStr);
+ j = parseLongFromChineseNumber(jStr, jStr.length());
}
if (StrUtil.isNotBlank(fStr)) {
- f = parseFromChineseNumber(fStr);
+ f = parseLongFromChineseNumber(fStr, fStr.length());
}
BigDecimal amount = new BigDecimal(y);
@@ -209,6 +172,76 @@ public class ChineseNumberParser {
return amount;
}
+ /**
+ * 把中文整数转换为数字 如 二百二十 220
+ *
+ * - 一百一十二 -》 112
+ * - 一千零一十二 -》 1012
+ *
+ *
+ * @param chinese 中文字符
+ * @param toIndex 结束位置(不包括),如果提供的是整数,这个为length(),小数则是“点”的位置
+ * @return 数字
+ */
+ public static long parseLongFromChineseNumber(final String chinese, final int toIndex) {
+ long result = 0;
+
+ // 节总和
+ long section = 0;
+ long number = 0;
+ ChineseUnit unit = null;
+ char c;
+ for (int i = 0; i < toIndex; i++) {
+ c = chinese.charAt(i);
+ final int num = chineseToNumber(c);
+ if (num >= 0) {
+ if (num == 0) {
+ // 遇到零时节结束,权位失效,比如两万二零一十
+ if (number > 0 && null != unit) {
+ section += number * (unit.value / 10);
+ }
+ unit = null;
+ } else if (number > 0) {
+ // 多个数字同时出现,报错
+ throw new IllegalArgumentException(StrUtil.format("Bad number '{}{}' at: {}", chinese.charAt(i - 1), c, i));
+ }
+ // 普通数字
+ number = num;
+ } else {
+ unit = chineseToUnit(c);
+ if (null == unit) {
+ // 出现非法字符
+ throw new IllegalArgumentException(StrUtil.format("Unknown unit '{}' at: {}", c, i));
+ }
+
+ //单位
+ if (unit.secUnit) {
+ // 节单位,按照节求和
+ section = (section + number) * unit.value;
+ result += section;
+ section = 0;
+ } else {
+ // 非节单位,和单位前的单数字组合为值
+ long unitNumber = number;
+ if (0 == number && 0 == i) {
+ // issue#1726,对于单位开头的数组,默认赋予1
+ // 十二 -> 一十二
+ // 百二 -> 一百二
+ unitNumber = 1;
+ }
+ section += (unitNumber * unit.value);
+ }
+ number = 0;
+ }
+ }
+
+ if (number > 0 && null != unit) {
+ number = number * (unit.value / 10);
+ }
+
+ return result + section + number;
+ }
+
/**
* 查找对应的权对象
*
diff --git a/hutool-core/src/test/java/org/dromara/hutool/core/math/ChineseNumberFormatterTest.java b/hutool-core/src/test/java/org/dromara/hutool/core/math/ChineseNumberFormatterTest.java
index 842fe329f..579e6d8b5 100644
--- a/hutool-core/src/test/java/org/dromara/hutool/core/math/ChineseNumberFormatterTest.java
+++ b/hutool-core/src/test/java/org/dromara/hutool/core/math/ChineseNumberFormatterTest.java
@@ -281,51 +281,6 @@ public class ChineseNumberFormatterTest {
Assertions.assertEquals('A', s);
}
- @Test
- public void chineseToNumberTest(){
- Assertions.assertEquals(0, ChineseNumberParser.parseFromChinese("零"));
- Assertions.assertEquals(102, ChineseNumberParser.parseFromChinese("一百零二"));
- Assertions.assertEquals(112, ChineseNumberParser.parseFromChinese("一百一十二"));
- Assertions.assertEquals(1012, ChineseNumberParser.parseFromChinese("一千零一十二"));
- Assertions.assertEquals(1000000, ChineseNumberParser.parseFromChinese("一百万"));
- Assertions.assertEquals(2000100112, ChineseNumberParser.parseFromChinese("二十亿零一十万零一百一十二"));
- }
-
- @Test
- public void chineseToNumberTest2(){
- Assertions.assertEquals(120, ChineseNumberParser.parseFromChinese("一百二"));
- Assertions.assertEquals(1200, ChineseNumberParser.parseFromChinese("一千二"));
- Assertions.assertEquals(22000, ChineseNumberParser.parseFromChinese("两万二"));
- Assertions.assertEquals(22003, ChineseNumberParser.parseFromChinese("两万二零三"));
- Assertions.assertEquals(22010, ChineseNumberParser.parseFromChinese("两万二零一十"));
- }
-
- @Test
- public void chineseToNumberTest3(){
- // issue#1726,对于单位开头的数组,默认赋予1
- // 十二 -> 一十二
- // 百二 -> 一百二
- Assertions.assertEquals(12, ChineseNumberParser.parseFromChinese("十二"));
- Assertions.assertEquals(120, ChineseNumberParser.parseFromChinese("百二"));
- Assertions.assertEquals(1300, ChineseNumberParser.parseFromChinese("千三"));
- }
-
- @Test
- public void badNumberTest(){
- Assertions.assertThrows(IllegalArgumentException.class, ()->{
- // 连续数字检查
- ChineseNumberParser.parseFromChinese("一百一二三");
- });
- }
-
- @Test
- public void badNumberTest2(){
- Assertions.assertThrows(IllegalArgumentException.class, ()->{
- // 非法字符
- ChineseNumberParser.parseFromChinese("一百你三");
- });
- }
-
@Test
public void singleMoneyTest(){
String format = ChineseNumberFormatter.of().setMoneyMode(true).format(0.01);
@@ -359,27 +314,4 @@ public class ChineseNumberFormatterTest {
format = ChineseNumberFormatter.of().format(1.02);
Assertions.assertEquals("一点零二", format);
}
-
- @SuppressWarnings("ConstantConditions")
- @Test
- public void testChineseMoneyToNumber(){
- /*
- * s=陆万柒仟伍佰伍拾陆圆, n=67556
- * s=陆万柒仟伍佰伍拾陆元, n=67556
- * s=叁角, n=0.3
- * s=贰分, n=0.02
- * s=陆万柒仟伍佰伍拾陆元叁角, n=67556.3
- * s=陆万柒仟伍佰伍拾陆元贰分, n=67556.02
- * s=叁角贰分, n=0.32
- * s=陆万柒仟伍佰伍拾陆元叁角贰分, n=67556.32
- */
- Assertions.assertEquals(67556, ChineseNumberParser.parseFromChineseMoney("陆万柒仟伍佰伍拾陆圆").longValue());
- Assertions.assertEquals(67556, ChineseNumberParser.parseFromChineseMoney("陆万柒仟伍佰伍拾陆元").longValue());
- Assertions.assertEquals(0.3D, ChineseNumberParser.parseFromChineseMoney("叁角").doubleValue(), 0);
- Assertions.assertEquals(0.02, ChineseNumberParser.parseFromChineseMoney("贰分").doubleValue(), 0);
- Assertions.assertEquals(67556.3, ChineseNumberParser.parseFromChineseMoney("陆万柒仟伍佰伍拾陆元叁角").doubleValue(), 0);
- Assertions.assertEquals(67556.02, ChineseNumberParser.parseFromChineseMoney("陆万柒仟伍佰伍拾陆元贰分").doubleValue(), 0);
- Assertions.assertEquals(0.32, ChineseNumberParser.parseFromChineseMoney("叁角贰分").doubleValue(), 0);
- Assertions.assertEquals(67556.32, ChineseNumberParser.parseFromChineseMoney("陆万柒仟伍佰伍拾陆元叁角贰分").doubleValue(), 0);
- }
}
diff --git a/hutool-core/src/test/java/org/dromara/hutool/core/math/ChineseNumberParserTest.java b/hutool-core/src/test/java/org/dromara/hutool/core/math/ChineseNumberParserTest.java
new file mode 100644
index 000000000..36dbfe529
--- /dev/null
+++ b/hutool-core/src/test/java/org/dromara/hutool/core/math/ChineseNumberParserTest.java
@@ -0,0 +1,88 @@
+package org.dromara.hutool.core.math;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.math.BigDecimal;
+
+public class ChineseNumberParserTest {
+ @Test
+ public void chineseToNumberTest(){
+ Assertions.assertEquals(0, parseFromChinese("零"));
+ Assertions.assertEquals(102, parseFromChinese("一百零二"));
+ Assertions.assertEquals(112, parseFromChinese("一百一十二"));
+ Assertions.assertEquals(1012, parseFromChinese("一千零一十二"));
+ Assertions.assertEquals(1000000, parseFromChinese("一百万"));
+ Assertions.assertEquals(2000100112, parseFromChinese("二十亿零一十万零一百一十二"));
+ }
+
+ @Test
+ public void chineseToNumberTest2(){
+ Assertions.assertEquals(120, parseFromChinese("一百二"));
+ Assertions.assertEquals(1200, parseFromChinese("一千二"));
+ Assertions.assertEquals(22000, parseFromChinese("两万二"));
+ Assertions.assertEquals(22003, parseFromChinese("两万二零三"));
+ Assertions.assertEquals(22010, parseFromChinese("两万二零一十"));
+ }
+
+ @Test
+ public void chineseToNumberTest3(){
+ // issue#1726,对于单位开头的数组,默认赋予1
+ // 十二 -> 一十二
+ // 百二 -> 一百二
+ Assertions.assertEquals(12, parseFromChinese("十二"));
+ Assertions.assertEquals(120, parseFromChinese("百二"));
+ Assertions.assertEquals(1300, parseFromChinese("千三"));
+ }
+
+ @Test
+ public void badNumberTest(){
+ Assertions.assertThrows(IllegalArgumentException.class, ()->{
+ // 连续数字检查
+ ChineseNumberParser.parseFromChinese("一百一二三");
+ });
+ }
+
+ @Test
+ public void badNumberTest2(){
+ Assertions.assertThrows(IllegalArgumentException.class, ()->{
+ // 非法字符
+ ChineseNumberParser.parseFromChinese("一百你三");
+ });
+ }
+
+ @Test
+ public void testChineseMoneyToNumber(){
+ /*
+ * s=陆万柒仟伍佰伍拾陆圆, n=67556
+ * s=陆万柒仟伍佰伍拾陆元, n=67556
+ * s=叁角, n=0.3
+ * s=贰分, n=0.02
+ * s=陆万柒仟伍佰伍拾陆元叁角, n=67556.3
+ * s=陆万柒仟伍佰伍拾陆元贰分, n=67556.02
+ * s=叁角贰分, n=0.32
+ * s=陆万柒仟伍佰伍拾陆元叁角贰分, n=67556.32
+ */
+ Assertions.assertEquals(67556, parseFromChinese("陆万柒仟伍佰伍拾陆圆"));
+ Assertions.assertEquals(67556, parseFromChinese("陆万柒仟伍佰伍拾陆元"));
+ Assertions.assertEquals(0.3D, parseFromChinese("叁角"), 0);
+ Assertions.assertEquals(0.02, parseFromChinese("贰分"), 0);
+ Assertions.assertEquals(67556.3, parseFromChinese("陆万柒仟伍佰伍拾陆元叁角"), 0);
+ Assertions.assertEquals(67556.02, parseFromChinese("陆万柒仟伍佰伍拾陆元贰分"), 0);
+ Assertions.assertEquals(0.32, parseFromChinese("叁角贰分"), 0);
+ Assertions.assertEquals(67556.32, parseFromChinese("陆万柒仟伍佰伍拾陆元叁角贰分"), 0);
+ }
+
+ @Test
+ void parseFromChineseNumberTest() {
+ BigDecimal i = ChineseNumberParser.parseFromChineseNumber("十二点二三");
+ Assertions.assertEquals(NumberUtil.toBigDecimal(12.23D), i);
+
+ i = ChineseNumberParser.parseFromChineseNumber("三点一四一五九二六五四");
+ Assertions.assertEquals(NumberUtil.toBigDecimal(3.141592654D), i);
+ }
+
+ private double parseFromChinese(final String str){
+ return ChineseNumberParser.parseFromChinese(str).doubleValue();
+ }
+}