Java数字转中文大写工具类详细代码(拿去即用)

 更新时间:2024年05月20日 09:44:11   作者:UnIQUE Eason  
最近项目中用到金额转大写的地方,索性给大家总结下,这篇文章主要给大家介绍了关于Java数字转中文大写工具类的相关资料,文中给出了详细的代码示例,需要的朋友可以参考下

工具类包括:

  • 数字转中文大写形式,比如一百二十一
  • 数字转金额用的大写形式,比如:壹佰贰拾壹
  • 转金额形式,比如:壹佰贰拾壹整
package cn.hutool.core.convert;

import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;

/**
 * 数字转中文类
 **/
public class NumberChineseFormatterUtils {

	/**
	 * 中文形式,奇数位置是简体,偶数位置是记账繁体,0共用
	 * 使用混合数组提高效率和数组复用
	 **/
	private static final char[] DIGITS = {'零', '一', '壹', '二', '贰', '三', '叁', '四', '肆', '五', '伍',
			'六', '陆', '七', '柒', '八', '捌', '九', '玖'};

	/**
	 * 汉字转阿拉伯数字的
	 */
	private static final ChineseUnit[] CHINESE_NAME_VALUE = {
			new ChineseUnit(' ', 1, false),
			new ChineseUnit('十', 10, false),
			new ChineseUnit('拾', 10, false),
			new ChineseUnit('百', 100, false),
			new ChineseUnit('佰', 100, false),
			new ChineseUnit('千', 1000, false),
			new ChineseUnit('仟', 1000, false),
			new ChineseUnit('万', 1_0000, true),
			new ChineseUnit('亿', 1_0000_0000, true),
	};


	/**
	 * 阿拉伯数字转换成中文,小数点后四舍五入保留两位. 使用于整数、小数的转换.
	 *
	 * @param amount           数字
	 * @param isUseTraditional 是否使用繁体
	 * @return 中文
	 */
	public static String format(double amount, boolean isUseTraditional) {
		return format(amount, isUseTraditional, false);
	}

	/**
	 * 阿拉伯数字转换成中文,小数点后四舍五入保留两位. 使用于整数、小数的转换.
	 *
	 * @param amount           数字
	 * @param isUseTraditional 是否使用繁体
	 * @param isMoneyMode      是否为金额模式
	 * @return 中文
	 */
	public static String format(double amount, boolean isUseTraditional, boolean isMoneyMode) {
		if (amount > 99_9999_9999_9999.99 || amount < -99999999999999.99) {
			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);

		final int numFen = (int) (temp % 10);
		temp = temp / 10;
		final int numJiao = (int) (temp % 10);
		temp = temp / 10;

		final StringBuilder chineseStr = new StringBuilder(longToChinese(temp, isUseTraditional));
		//负数
		if (negative) { // 整数部分不为 0
			chineseStr.insert(0, "负");
		}

		// 小数部分
		if (numFen != 0 || numJiao != 0) {
			if (numFen == 0) {
				chineseStr.append(isMoneyMode ? "元" : "点").append(numberToChinese(numJiao, isUseTraditional)).append(isMoneyMode ? "角" : "");
			} else { // “分”数不为 0
				if (numJiao == 0) {
					chineseStr.append(isMoneyMode ? "元零" : "点零").append(numberToChinese(numFen, isUseTraditional)).append(isMoneyMode ? "分" : "");
				} else {
					chineseStr.append(isMoneyMode ? "元" : "点").append(numberToChinese(numJiao, isUseTraditional)).append(isMoneyMode ? "角" : "").append(numberToChinese(numFen, isUseTraditional)).append(isMoneyMode ? "分" : "");
				}
			}
		} else if (isMoneyMode) {
			//无小数部分的金额结尾
			chineseStr.append("元整");
		}

		return chineseStr.toString();
	}

	/**
	 * 数字字符转中文,非数字字符原样返回
	 *
	 * @param c                数字字符
	 * @param isUseTraditional 是否繁体
	 * @return 中文字符
	 * @since 5.3.9
	 */
	public static String numberCharToChinese(char c, boolean isUseTraditional) {
		if (c < '0' || c > '9') {
			return String.valueOf(c);
		}
		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 则返回 ""
	 *
	 * @param amountPart       数字部分
	 * @param isUseTraditional 是否使用繁体单位
	 * @return 转换后的汉字
	 */
	private static String thousandToChinese(int amountPart, boolean isUseTraditional) {
		int temp = amountPart;

		StringBuilder chineseStr = new StringBuilder();
		boolean lastIsZero = true; // 在从低位往高位循环时,记录上一位数字是不是 0
		for (int i = 0; temp > 0; i++) {
			int digit = temp % 10;
			if (digit == 0) { // 取到的数字为 0
				if (false == lastIsZero) {
					// 前一个数字不是 0,则在当前汉字串前加“零”字;
					chineseStr.insert(0, "零");
				}
				lastIsZero = true;
			} else { // 取到的数字不是 0
				chineseStr.insert(0, numberToChinese(digit, isUseTraditional) + getUnitName(i, isUseTraditional));
				lastIsZero = false;
			}
			temp = temp / 10;
		}
		return chineseStr.toString();
	}

	/**
	 * 把中文转换为数字 如 二百二十 220
	 * 
	 *  一百一十二 -》 112
	 *  一千零一十二 -》 1012
	 *
	 * @param chinese 中文字符
	 * @return 数字
	 */
	public static int chineseToNumber(String chinese) {
		final int length = chinese.length();
		int result = 0;

		// 节总和
		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));
				}

				//单位
				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 (number > 0 && null != unit) {
			number = number * (unit.value / 10);
		}

		return result + section + number;
	}

	/**
	 * 查找对应的权对象
	 *
	 * @param chinese 中文权位名
	 * @return 权对象
	 */
	private static ChineseUnit chineseToUnit(char chinese) {
		for (ChineseUnit chineseNameValue : CHINESE_NAME_VALUE) {
			if (chineseNameValue.name == chinese) {
				return chineseNameValue;
			}
		}
		return null;
	}

	/**
	 * 将汉字单个数字转换为int类型数字
	 *
	 * @param chinese 汉字数字,支持简体和繁体
	 * @return 数字,-1表示未找到
	 */
	private static int chineseToNumber(char chinese) {
		if ('两' == chinese) {
			// 口语纠正
			chinese = '二';
		}
		final int i = ArrayUtil.indexOf(DIGITS, chinese);
		if (i > 0) {
			return (i + 1) / 2;
		}
		return i;
	}

	/**
	 * 单个数字转汉字
	 *
	 * @param number           数字
	 * @param isUseTraditional 是否使用繁体
	 * @return 汉字
	 */
	private static char numberToChinese(int number, boolean isUseTraditional) {
		if (0 == number) {
			return DIGITS[0];
		}
		return DIGITS[number * 2 - (isUseTraditional ? 0 : 1)];
	}

	/**
	 * 获取对应级别的单位
	 *
	 * @param index            级别,0表示各位,1表示十位,2表示百位,以此类推
	 * @param isUseTraditional 是否使用繁体
	 * @return 单位
	 */
	private static String getUnitName(int index, boolean isUseTraditional) {
		if (0 == index) {
			return StrUtil.EMPTY;
		}
		return String.valueOf(CHINESE_NAME_VALUE[index * 2 - (isUseTraditional ? 0 : 1)].name);
	}

	/**
	 * 权位
	 */
	private static class ChineseUnit {
		/**
		 * 中文权名称
		 */
		private final char name;
		/**
		 * 10的倍数值
		 */
		private final int value;
		/**
		 * 是否为节权位,它不是与之相邻的数字的倍数,而是整个小节的倍数。

		 * 例如二十三万,万是节权位,与三无关,而和二十三关联
		 */
		private final boolean secUnit;

		/**
		 * 构造
		 *
		 * @param name    名称
		 * @param value   值,即10的倍数
		 * @param secUnit 是否为节权位
		 */
		public ChineseUnit(char name, int value, boolean secUnit) {
			this.name = name;
			this.value = value;
			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, '零');
		}
	}
}

注意:以上代码用到hutool工具,需要引入依赖:

		<dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.5</version>
        </dependency>

附:数字转换

// 数字转英文
double a = 23.56;
// TWENTY THREE AND CENTS FIFTY SIX ONLY
String a1 = Convert.numberToWord(a);
 
// 数字转中文
long b = 345673;
// 跟Convert.digitToChinese方法一样,第二个参数如果设为false,就是中文的一二三四...
String s = Convert.numberToChinese(b, true);
 
// 数字简化
long b = 345673;
// 34.57w
String s = Convert.numberToSimple(b);
log.info(s);
// 1.2k
String s1 = Convert.numberToSimple(1200);
log.info(s1);
 
// 中文数字转数字
// 1012
int b = Convert.chineseToNumber("一千零一十二");
log.info(b + "");

总结 

到此这篇关于Java数字转中文大写工具类的文章就介绍到这了,更多相关Java数字转中文大写工具类内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Nacos与SpringBoot实现配置管理的开发实践

    Nacos与SpringBoot实现配置管理的开发实践

    在微服务架构中,配置管理是一个核心组件,而Nacos为此提供了一个强大的解决方案,本文主要介绍了Nacos与SpringBoot实现配置管理的开发实践,具有一定的参考价值
    2023-08-08
  • SpringBoot整合MinIO实现文件上传的方法详解

    SpringBoot整合MinIO实现文件上传的方法详解

    一般涉及到文件上传,基本上都是保存在项目本地,这种方式比较省事,但是安全性不高。所以今天给大伙详细介绍一些如何利用MinIO实现文件上传,感兴趣的可以了解一下
    2022-05-05
  • arthas jprofiler做复杂链路的调用分析

    arthas jprofiler做复杂链路的调用分析

    这篇文章主要为大家介绍了arthas jprofiler做复杂链路的调用分析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-06-06
  • java单例模式学习示例

    java单例模式学习示例

    java中单例模式是一种常见的设计模式,单例模式分三种:懒汉式单例、饿汉式单例、登记式单例三种,下面提供了单例模式的示例
    2014-01-01
  • SpringBoot使用Thymeleaf自定义标签的实例代码

    SpringBoot使用Thymeleaf自定义标签的实例代码

    这篇文章主要介绍了SpringBoot使用Thymeleaf自定义标签的实例代码,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-09-09
  • Spring Boot整合SSE实时通信的问题小结

    Spring Boot整合SSE实时通信的问题小结

    本文介绍了服务器发送事件(Server-Sent Events,SSE)技术,其主要特点包括单向数据流、自动重连、自定义事件类型等,SSE适用于实时更新场景,如新闻推送、评论系统等,感兴趣的朋友跟随小编一起看看吧
    2025-01-01
  • 详解springboot解决CORS跨域的三种方式

    详解springboot解决CORS跨域的三种方式

    本文主要介绍了详解springboot解决CORS跨域的三种方式,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-07-07
  • JAVA中String介绍及常见面试题小结

    JAVA中String介绍及常见面试题小结

    这篇文章主要介绍了JAVA中String介绍及常见面试题,在java面试中经常会被面试官问到,小编通过实例代码相结合给大家详细介绍,需要的朋友可以参考下
    2020-02-02
  • Java源码解析之HashMap的put、resize方法详解

    Java源码解析之HashMap的put、resize方法详解

    这篇文章主要介绍了Java源码解析之HashMap的put、resize方法详解,文中有非常详细的代码示例,对正在学习java的小伙伴们有很大的帮助,需要的朋友可以参考下
    2021-04-04
  • 在SpringBoot接口中正确地序列化时间字段的方法

    在SpringBoot接口中正确地序列化时间字段的方法

    文章主要介绍在 Spring Boot 接口中正确序列化时间字段的方法,包括 Java 中Date和LocalDateTime类型的区别,JSON 序列化和请求参数中时间字段的处理,如时间字符串的格式配置、时间戳的使用及相关配置,还提到了在 Swagger UI 中的类型设置,需要的朋友可以参考下
    2024-11-11

最新评论