深圳幻海软件技术有限公司 欢迎您!

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

2023-09-05

Java阿拉伯数字转汉字网上看过很多实现,但都有BUG,不是多余0没有处理,就是很多生成的汉字字符串根本不符合中文阅读习惯(各位代码写完测试的时候用例多搞一点啊)刚好公司有个项目就要实现这个小功能,顾把自己的实现分享出来1、结果示例看代码前,先观察一下结果是否满足各位的预期用例输入:int[]num

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、思路讲解

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

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

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
文章知识点与官方知识档案匹配,可进一步学习相关知识
算法技能树首页概览51117 人正在系统学习中