• 【Java】阿拉伯数字转汉字(完全符合中文阅读习惯)(支持所有整数类型)


    Java 阿拉伯数字转汉字

    网上看过很多实现,但都有 BUG,不是多余0没有处理,就是很多生成的汉字字符串根本不符合中文阅读习惯(各位代码写完测试的时候用例多搞一点啊)

    刚好公司有个项目就要实现这个小功能,顾把自己的实现分享出来


    1、结果示例

    看代码前,先观察一下结果是否满足各位的预期

    用例输入:

    int[] nums = {1000, 1024, 10, 100024, 92030003, 90000003, 1, 2, 3, 11};
    
    • 1

    用例输出:

    一千
    一千零二十四
    十
    十万零二十四
    九千二百零三万零三
    九千万零三
    一
    二
    三
    十一
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    用例基本能覆盖所有特殊场景,支持 Integer.MIN_VALUE - Integer.MAX_VALUE 范围内的所有输入

    小数场景没做,因为暂时没这个需要,不过小数场景要更好做一些,毕竟按照中文阅读习惯,小数只要把小数点后的数字一一列出来转换为中文字符就行,没有位数转换的需求

    3.14159 -> 三点一四一五九
    
    • 1

    有小数转换需求的同学,只要把整数部分取出来,用我的代码转换,小数部分再写个简单的函数实现即可

    可能有人习惯把"二"读成"两"的,到时候在代码里替换字符就行


    2、完整代码

    急着用的同学,写个工具类,直接拷贝过去就能用了

    /**
     * @ClassName NumStrConvertor
     * @Description 阿拉伯数字转中文
     * @Author faro_z
     * @Date 2022/12/5 18:07
     * @Version 1.0
     **/
    public class NumStrConvertor {
        public static void main(String[] args) {
            int[] nums = {1000, 1024, 10, 100024, 92030003, 90000003, 1, 2, 3, 11};
            for (int num : nums) {
                System.out.println(cnNumConvertor(num));
            }
        }
    
        /**
         * 转换阿拉伯数字到中文字符
         *
         * @param num
         * @return
         */
        public static String cnNumConvertor(int num) {
            if (num==0) {
                return "零";
            }
            final String illegalPrefix = "一十";
            final String[] units = {"", "万", "亿", "兆"};
            String prefix = "";
            if (num < 0) {
                prefix = "负";
                num = -num;
            }
            String numStr = String.valueOf(num);
            final int metaLen = 4;
            int r = numStr.length() - 1;
            int l = Math.max(0, r - metaLen + 1);
            int unitIndex = 0;
            StringBuilder builder = new StringBuilder();
            while (r >= 0 && l >= 0) {
                builder.insert(0, cnNumMetaConvertor(numStr.substring(l, r + 1)) + units[unitIndex++]);
                r = l - 1;
                l = Math.max(0, r - metaLen + 1);
            }
            String res = builder.toString();
            if (res.startsWith(illegalPrefix)) {
                res = res.substring(1, res.length());
            }
            return prefix + res;
        }
    
        /**
         * 按照中文阅读习惯,处理最高4位的 meta 数字集
         *
         * @param num
         * @return
         */
        private static String cnNumMetaConvertor(String num) {
            final int metaLen = 4;
            final String zeroStr = "0";
            final String[] cnNums = {"零", "一", "二", "三", "四", "五", "六", "七", "八", "九"};
            final String[] units = {"千", "百", "十", ""};
            StringBuilder builder = new StringBuilder();
            int N = num.length();
            boolean canNotJudge = N > metaLen || (N < metaLen && num.startsWith(zeroStr));
            if (canNotJudge) {
                return "";
            }
            boolean isPreZero = false;
            int unitIndex = metaLen - num.length() - 1;
            for (int i = 0; i < N; i++) {
                ++unitIndex;
                final int currNum = num.charAt(i) - '0';
                final String currNumCn = cnNums[currNum];
                final String currUnit = units[unitIndex];
                if (currNum == 0) {
                    if (isPreZero) {
                        continue;
                    }
                    isPreZero = true;
                    builder.append(currNumCn);
                } else {
                    isPreZero = false;
                    builder.append(currNumCn);
                    builder.append(currUnit);
                }
            }
            String res = builder.toString();
            return res.endsWith("零") ? res.substring(0, res.length() - 1) : res;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91

    3、思路讲解

    按照中文阅读习惯,我们习惯将每四位分为一个单元做阅读,比如如下示例:

    image-20221205223236630

    因为每四位的处理逻辑都是一样的,所以这部分我们可以单独拆出来处理:

    private static String cnNumMetaConvertor(String num) {
            final int metaLen = 4;
            final String zeroStr = "0";
            final String[] cnNums = {"零", "一", "二", "三", "四", "五", "六", "七", "八", "九"};
            final String[] units = {"千", "百", "十", ""};
            StringBuilder builder = new StringBuilder();
            int N = num.length();
      			// 大于四位或者  小于四位,但是开头的数字为0,都不满足处理条件
            boolean canNotJudge = N > metaLen || (N < metaLen && num.startsWith(zeroStr));
            if (canNotJudge) {
                return "";
            }
      			// 用来标识前一个字符是否为0,避免出现重复打印“零”的情况
            boolean isPreZero = false;
            int unitIndex = metaLen - num.length() - 1;
            for (int i = 0; i < N; i++) {
                ++unitIndex;
                final int currNum = num.charAt(i) - '0';
                final String currNumCn = cnNums[currNum];
                final String currUnit = units[unitIndex];
                if (currNum == 0) {
                    if (isPreZero) {
                        continue;
                    }
                    isPreZero = true;
                    builder.append(currNumCn);
                } else {
                    isPreZero = false;
                    builder.append(currNumCn);
                    builder.append(currUnit);
                }
            }
            String res = builder.toString();
            return res.endsWith("零") ? res.substring(0, res.length() - 1) : res;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    接着,就是将“每个四位”进行拼接,并依次在后面加上,万、亿、兆的单位:

    这里使用双指针法进行范围截取,以 4 为单位截取字符串,然后使用上面的函数处理

    cnNumConvertor(num) {
      // 负数和非0判断
      // ...
      res=""
      while() {
        currSplit = split(num)
    	  currSplitCN = cnNumMetaConvertor(currSplit)
        res+= currSplitCN + 单位
      }
      // 特殊字符处理
      // ...
     	return res
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    同时外层函数我们还要做个负数和0的判断

    负数的话,要转换成正数处理,最后返回结果时加上"负"的前缀

    0 直接返回 “零”

    public static String cnNumConvertor(int num) {
            if (num==0) {
                return "零";
            }
            final String illegalPrefix = "一十";
            final String[] units = {"", "万", "亿", "兆"};
            String prefix = "";
            if (num < 0) {
                prefix = "负";
                num = -num;
            }
            String numStr = String.valueOf(num);
            final int metaLen = 4;
            int r = numStr.length() - 1;
            int l = Math.max(0, r - metaLen + 1);
            int unitIndex = 0;
            StringBuilder builder = new StringBuilder();
            while (r >= 0 && l >= 0) {
                builder.insert(0, cnNumMetaConvertor(numStr.substring(l, r + 1)) + units[unitIndex++]);
                r = l - 1;
                l = Math.max(0, r - metaLen + 1);
            }
            String res = builder.toString();
      			// 对一十,一十万,一十四这类结果做处理
            if (res.startsWith(illegalPrefix)) {
                res = res.substring(1, res.length());
            }
            return prefix + res;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    特殊结果处理:

    上述代码本身能完成很好的结果生成,但是还是有部分结果不满足中文阅读习惯

    比如 对一十,一十万,一十四这类结果,只要将前缀的 “一” 去掉即可

    if (res.startsWith(illegalPrefix)) {
      res = res.substring(1, res.length());
    }
    
    • 1
    • 2
    • 3
  • 相关阅读:
    Lambda表达式超详细总结
    时区的坑,别再踩了!
    redis Lettuce客户端
    记录一次htonl和ntohl的使用方法和差别
    数据结构:排序算法
    海康Visionmaster-全局脚本:通过通讯触发快速匹配 模块换型的方法
    语义分割实战:基于tensorflow搭建DeeplabV3实现语义分割任务
    报错:npm ERR code EPERM
    RabbitMQ之延迟队列
    深入理解Java并发锁
  • 原文地址:https://blog.csdn.net/weixin_44062380/article/details/128194856