This commit is contained in:
Looly
2024-04-21 12:20:57 +08:00
parent e05470acea
commit 606f690470
4 changed files with 182 additions and 129 deletions

View File

@@ -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);
}

View File

@@ -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<br>
* 把中文转换为数字<br>
* <ul>
* <li>一百一十二 -》 112</li>
* <li>一千零一十二 -》 1012</li>
* <li>十二点二三 -》 12.23</li>
* <li>三点一四一五九二六五四 -》 3.141592654</li>
* </ul>
*
* @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<br>
* <ul>
* <li>一百一十二 -》 112</li>
* <li>一千零一十二 -》 1012</li>
* </ul>
*
* @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;
}
/**
* 查找对应的权对象
*

View File

@@ -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);
}
}

View File

@@ -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();
}
}