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
+ * 把中文转换为数字
* * * @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
+ * + * + * @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(); + } +}