diff --git a/CHANGELOG.md b/CHANGELOG.md index 3b805ae2a..fb36e430f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ ------------------------------------------------------------------------------------------------------------- -# 5.7.8 (2021-08-07) +# 5.7.8 (2021-08-08) ### 🐣新特性 * 【core 】 MapProxy支持return this的setter方法(pr#392@Gitee) @@ -11,6 +11,7 @@ * 【core 】 增加compress包,扩充Zip操作灵活性 ### 🐞Bug修复 +* 【core 】 改进NumberChineseFormatter算法,补充完整单元测试,解决零问题 ------------------------------------------------------------------------------------------------------------- diff --git a/hutool-core/src/main/java/cn/hutool/core/convert/NumberChineseFormatter.java b/hutool-core/src/main/java/cn/hutool/core/convert/NumberChineseFormatter.java index 6fc3daa87..337a5b012 100644 --- a/hutool-core/src/main/java/cn/hutool/core/convert/NumberChineseFormatter.java +++ b/hutool-core/src/main/java/cn/hutool/core/convert/NumberChineseFormatter.java @@ -34,8 +34,8 @@ public class NumberChineseFormatter { new ChineseUnit('佰', 100, false), new ChineseUnit('千', 1000, false), new ChineseUnit('仟', 1000, false), - new ChineseUnit('万', 10000, true), - new ChineseUnit('亿', 100000000, true), + new ChineseUnit('万', 1_0000, true), + new ChineseUnit('亿', 1_0000_0000, true), }; @@ -63,71 +63,22 @@ public class NumberChineseFormatter { throw new IllegalArgumentException("Number support only: (-99999999999999.99 ~ 99999999999999.99)!"); } + // 负数 boolean negative = false; if (amount < 0) { negative = true; amount = -amount; } + // 分和角 long temp = Math.round(amount * 100); - int numFen = (int) (temp % 10); + + final int numFen = (int) (temp % 10); temp = temp / 10; - int numJiao = (int) (temp % 10); + final int numJiao = (int) (temp % 10); temp = temp / 10; - //将数字以万为单位分为多份 - int[] parts = new int[20]; - int partsCount = 0; - for (int i = 0; temp != 0; i++) { - int part = (int) (temp % 10000); - parts[i] = part; - partsCount++; - temp = temp / 10000; - } - - boolean underWanIsZero = true; // 标志“万”下面一级是不是 0 - - StringBuilder chineseStr = new StringBuilder(); - for (int i = 0; i < partsCount; i++) { - final String partChinese = toChinese(parts[i], isUseTraditional); - if (i % 2 == 0) { - underWanIsZero = StrUtil.isEmpty(partChinese); - } - - // TODO 此处逻辑过于复杂,等待整理重构 - if (i != 0) { - if (i % 2 == 0) { - if (parts[i - 1] < 1000 && parts[i - 1] > 0) { - // 如果"亿"的部分不为 0, 而"亿"以下的部分小于 1000,则亿后面应该跟“零”,如一亿零三十五万 - chineseStr.insert(0, "零"); - } - chineseStr.insert(0, "亿"); - } else { - if (StrUtil.isEmpty(partChinese) && false == underWanIsZero) { - // 如果“万”对应的 part 为 0,而“万”下面一级不为 0,则不加“万”,而加“零” - chineseStr.insert(0, "零"); - } else { - if (parts[i - 1] < 1000 && parts[i - 1] > 0) { - // 如果"万"的部分不为 0, 而"万"以下的部分小于 1000 大于 0, 则万后面应该跟“零”,如一万零三百 - chineseStr.insert(0, "零"); - } else if(parts[i] > 0 && parts[i] % 10 == 0){ - // 如果万的部分没有个位数,需跟“零”,如十万零八千 - chineseStr.insert(0, "零"); - } - if (parts[i] > 0) { - // 如果"万"的部分不为 0 则增加万 - chineseStr.insert(0, "万"); - } - } - } - } - chineseStr.insert(0, partChinese); - } - - // 整数部分为 0, 则表达为"零" - if (StrUtil.EMPTY.equals(chineseStr.toString())) { - chineseStr = new StringBuilder(String.valueOf(DIGITS[0])); - } + final StringBuilder chineseStr = new StringBuilder(longToChinese(temp, isUseTraditional)); //负数 if (negative) { // 整数部分不为 0 chineseStr.insert(0, "负"); @@ -150,7 +101,6 @@ public class NumberChineseFormatter { } return chineseStr.toString(); - } /** @@ -168,6 +118,95 @@ public class NumberChineseFormatter { return String.valueOf(numberToChinese(c - '0', isUseTraditional)); } + /** + * 阿拉伯数字整数部分转换成中文,只支持正数 + * + * @param amount 数字 + * @param isUseTraditional 是否使用繁体 + * @return 中文 + */ + private static String longToChinese(long amount, boolean isUseTraditional) { + if(0 == amount){ + return "零"; + } + + //将数字以万为单位分为多份 + int[] parts = new int[4]; + for (int i = 0; amount != 0; i++) { + parts[i] = (int) (amount % 10000); + amount = amount / 10000; + } + + final StringBuilder chineseStr = new StringBuilder(); + int partValue; + String partChinese; + + // 千 + partValue = parts[0]; + if(partValue > 0){ + partChinese = thousandToChinese(partValue, isUseTraditional); + chineseStr.insert(0, partChinese); + + if(partValue < 1000){ + // 和万位之间空0,则补零,如一万零三百 + addPreZero(chineseStr); + } + } + + // 万 + partValue = parts[1]; + if(partValue > 0){ + if((partValue % 10 == 0 && parts[0] > 0)){ + // 如果"万"的个位是0,则补零,如十万零八千 + addPreZero(chineseStr); + } + partChinese = thousandToChinese(partValue, isUseTraditional); + chineseStr.insert(0, partChinese + "万"); + + if(partValue < 1000){ + // 和亿位之间空0,则补零,如一亿零三百万 + addPreZero(chineseStr); + } + } else{ + addPreZero(chineseStr); + } + + // 亿 + partValue = parts[2]; + if(partValue > 0){ + if((partValue % 10 == 0 && parts[1] > 0)){ + // 如果"万"的个位是0,则补零,如十万零八千 + addPreZero(chineseStr); + } + + partChinese = thousandToChinese(partValue, isUseTraditional); + chineseStr.insert(0, partChinese + "亿"); + + if(partValue < 1000){ + // 和万亿位之间空0,则补零,如一万亿零三百亿 + addPreZero(chineseStr); + } + } else{ + addPreZero(chineseStr); + } + + // 万亿 + partValue = parts[3]; + if(partValue > 0){ + if(parts[2] == 0){ + chineseStr.insert(0, "亿"); + } + partChinese = thousandToChinese(partValue, isUseTraditional); + chineseStr.insert(0, partChinese + "万"); + } + + if(StrUtil.isNotEmpty(chineseStr) && '零' == chineseStr.charAt(0)){ + return chineseStr.substring(1); + } + + return chineseStr.toString(); + } + /** * 把一个 0~9999 之间的整数转换为汉字的字符串,如果是 0 则返回 "" * @@ -175,7 +214,7 @@ public class NumberChineseFormatter { * @param isUseTraditional 是否使用繁体单位 * @return 转换后的汉字 */ - private static String toChinese(int amountPart, boolean isUseTraditional) { + private static String thousandToChinese(int amountPart, boolean isUseTraditional) { int temp = amountPart; StringBuilder chineseStr = new StringBuilder(); @@ -365,4 +404,14 @@ public class NumberChineseFormatter { this.secUnit = secUnit; } } + + private static void addPreZero(StringBuilder chineseStr){ + if(StrUtil.isEmpty(chineseStr)){ + return; + } + final char c = chineseStr.charAt(0); + if('零' != c){ + chineseStr.insert(0, '零'); + } + } } diff --git a/hutool-core/src/test/java/cn/hutool/core/convert/NumberChineseFormatterTest.java b/hutool-core/src/test/java/cn/hutool/core/convert/NumberChineseFormatterTest.java index d091c3bcd..8f0d88828 100644 --- a/hutool-core/src/test/java/cn/hutool/core/convert/NumberChineseFormatterTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/convert/NumberChineseFormatterTest.java @@ -5,11 +5,115 @@ import org.junit.Test; public class NumberChineseFormatterTest { + // 测试千 + @Test + public void formatThousandLongTest(){ + String f = NumberChineseFormatter.format(0, false); + Assert.assertEquals("零", f); + f = NumberChineseFormatter.format(1, false); + Assert.assertEquals("一", f); + f = NumberChineseFormatter.format(10, false); + Assert.assertEquals("一十", f); + f = NumberChineseFormatter.format(12, false); + Assert.assertEquals("一十二", f); + f = NumberChineseFormatter.format(100, false); + Assert.assertEquals("一百", f); + f = NumberChineseFormatter.format(101, false); + Assert.assertEquals("一百零一", f); + f = NumberChineseFormatter.format(110, false); + Assert.assertEquals("一百一十", f); + f = NumberChineseFormatter.format(112, false); + Assert.assertEquals("一百一十二", f); + f = NumberChineseFormatter.format(1000, false); + Assert.assertEquals("一千", f); + f = NumberChineseFormatter.format(1001, false); + Assert.assertEquals("一千零一", f); + f = NumberChineseFormatter.format(1010, false); + Assert.assertEquals("一千零一十", f); + f = NumberChineseFormatter.format(1100, false); + Assert.assertEquals("一千一百", f); + f = NumberChineseFormatter.format(1101, false); + Assert.assertEquals("一千一百零一", f); + f = NumberChineseFormatter.format(9999, false); + Assert.assertEquals("九千九百九十九", f); + } + + // 测试万 + @Test + public void formatTenThousandLongTest(){ + String f = NumberChineseFormatter.format(1_0000, false); + Assert.assertEquals("一万", f); + f = NumberChineseFormatter.format(1_0001, false); + Assert.assertEquals("一万零一", f); + f = NumberChineseFormatter.format(1_0010, false); + Assert.assertEquals("一万零一十", f); + f = NumberChineseFormatter.format(1_0100, false); + Assert.assertEquals("一万零一百", f); + f = NumberChineseFormatter.format(1_1000, false); + Assert.assertEquals("一万一千", f); + f = NumberChineseFormatter.format(10_1000, false); + Assert.assertEquals("一十万零一千", f); + f = NumberChineseFormatter.format(10_0100, false); + Assert.assertEquals("一十万零一百", f); + f = NumberChineseFormatter.format(100_1000, false); + Assert.assertEquals("一百万零一千", f); + f = NumberChineseFormatter.format(100_0100, false); + Assert.assertEquals("一百万零一百", f); + f = NumberChineseFormatter.format(1000_1000, false); + Assert.assertEquals("一千万零一千", f); + f = NumberChineseFormatter.format(1000_0100, false); + Assert.assertEquals("一千万零一百", f); + f = NumberChineseFormatter.format(9999_0000, false); + Assert.assertEquals("九千九百九十九万", f); + } + + // 测试亿 + @Test + public void formatHundredMillionLongTest(){ + String f = NumberChineseFormatter.format(1_0000_0000L, false); + Assert.assertEquals("一亿", f); + f = NumberChineseFormatter.format(1_0000_0001L, false); + Assert.assertEquals("一亿零一", f); + f = NumberChineseFormatter.format(1_0000_1000L, false); + Assert.assertEquals("一亿零一千", f); + f = NumberChineseFormatter.format(1_0001_0000L, false); + Assert.assertEquals("一亿零一万", f); + f = NumberChineseFormatter.format(1_0010_0000L, false); + Assert.assertEquals("一亿零一十万", f); + f = NumberChineseFormatter.format(1_0010_0000L, false); + Assert.assertEquals("一亿零一十万", f); + f = NumberChineseFormatter.format(1_0100_0000L, false); + Assert.assertEquals("一亿零一百万", f); + f = NumberChineseFormatter.format(1_1000_0000L, false); + Assert.assertEquals("一亿一千万", f); + f = NumberChineseFormatter.format(10_1000_0000L, false); + Assert.assertEquals("一十亿零一千万", f); + f = NumberChineseFormatter.format(100_1000_0000L, false); + Assert.assertEquals("一百亿零一千万", f); + f = NumberChineseFormatter.format(1000_1000_0000L, false); + Assert.assertEquals("一千亿零一千万", f); + f = NumberChineseFormatter.format(1100_1000_0000L, false); + Assert.assertEquals("一千一百亿零一千万", f); + f = NumberChineseFormatter.format(9999_0000_0000L, false); + Assert.assertEquals("九千九百九十九亿", f); + } + + // 测试万亿 + @Test + public void formatTrillionsLongTest(){ + String f = NumberChineseFormatter.format(1_0000_0000_0000L, false); + Assert.assertEquals("一万亿", f); + f = NumberChineseFormatter.format(1_0000_1000_0000L, false); + Assert.assertEquals("一万亿零一千万", f); + f = NumberChineseFormatter.format(1_0010_0000_0000L, false); + Assert.assertEquals("一万零一十亿", f); + } + @Test public void formatTest() { - String f0 = NumberChineseFormatter.format(50008000, false); + String f0 = NumberChineseFormatter.format(5000_8000, false); Assert.assertEquals("五千万零八千", f0); - String f1 = NumberChineseFormatter.format(10889.72356, false); + String f1 = NumberChineseFormatter.format(1_0889.72356, false); Assert.assertEquals("一万零八百八十九点七二", f1); f1 = NumberChineseFormatter.format(12653, false); Assert.assertEquals("一万二千六百五十三", f1); @@ -73,14 +177,20 @@ public class NumberChineseFormatterTest { @Test public void digitToChineseTest() { - String digitToChinese = Convert.digitToChinese(12412412412421.12); + String digitToChinese = Convert.digitToChinese(12_4124_1241_2421.12); Assert.assertEquals("壹拾贰万肆仟壹佰贰拾肆亿壹仟贰佰肆拾壹万贰仟肆佰贰拾壹元壹角贰分", digitToChinese); - String digitToChinese2 = Convert.digitToChinese(12412412412421D); - Assert.assertEquals("壹拾贰万肆仟壹佰贰拾肆亿壹仟贰佰肆拾壹万贰仟肆佰贰拾壹元整", digitToChinese2); + digitToChinese = Convert.digitToChinese(12_0000_1241_2421L); + Assert.assertEquals("壹拾贰万亿零壹仟贰佰肆拾壹万贰仟肆佰贰拾壹元整", digitToChinese); - String digitToChinese3 = Convert.digitToChinese(2421.02); - Assert.assertEquals("贰仟肆佰贰拾壹元零贰分", digitToChinese3); + digitToChinese = Convert.digitToChinese(12_0000_0000_2421L); + Assert.assertEquals("壹拾贰万亿零贰仟肆佰贰拾壹元整", digitToChinese); + + digitToChinese = Convert.digitToChinese(12_4124_1241_2421D); + Assert.assertEquals("壹拾贰万肆仟壹佰贰拾肆亿壹仟贰佰肆拾壹万贰仟肆佰贰拾壹元整", digitToChinese); + + digitToChinese = Convert.digitToChinese(2421.02); + Assert.assertEquals("贰仟肆佰贰拾壹元零贰分", digitToChinese); } @Test @@ -93,8 +203,8 @@ public class NumberChineseFormatterTest { digitUppercase = Convert.digitToChinese(a); Assert.assertEquals("壹仟零贰拾肆元整", digitUppercase); - a = 1024; - digitUppercase = Convert.digitToChinese(a); + double b = 1024; + digitUppercase = Convert.digitToChinese(b); Assert.assertEquals("壹仟零贰拾肆元整", digitUppercase); } @@ -108,6 +218,12 @@ public class NumberChineseFormatterTest { Assert.assertEquals("贰万亿元整", digitToChinese); } + @Test + public void digitToChineseTest4() { + String digitToChinese = Convert.digitToChinese(400_0000.00); + Assert.assertEquals("肆佰万元整", digitToChinese); + } + @Test public void numberCharToChineseTest(){ String s = NumberChineseFormatter.numberCharToChinese('1', false);