382 lines
9.7 KiB
Java
382 lines
9.7 KiB
Java
package com.yem.wm.utils;
|
||
|
||
import kd.bos.dataentity.utils.StringUtils;
|
||
|
||
/**
|
||
* 中文汉字和阿拉伯数字相互转化帮助类
|
||
* @author zhangh
|
||
*
|
||
*/
|
||
public class NumberChineseUtil {
|
||
|
||
/**
|
||
* 中文形式,奇数位置是简体,偶数位置是记账繁体,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),
|
||
};
|
||
public static final String NULL = "null";
|
||
|
||
/**
|
||
* 把中文转换为数字
|
||
* @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;
|
||
}
|
||
// 普通数字
|
||
number = num;
|
||
} else {
|
||
unit = chineseToUnit(c);
|
||
if (null == unit) {
|
||
// 出现非法字符
|
||
break;
|
||
}
|
||
|
||
//单位
|
||
if (unit.isSecUnit()) {
|
||
// 节单位,按照节求和
|
||
section = (section + number) * unit.getValue();
|
||
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;
|
||
}
|
||
|
||
private static ChineseUnit chineseToUnit(char chinese) {
|
||
for (ChineseUnit chineseNameValue : CHINESE_NAME_VALUE) {
|
||
if (chineseNameValue.name != chinese)
|
||
continue;
|
||
return chineseNameValue;
|
||
}
|
||
return null;
|
||
}
|
||
|
||
private static int chineseToNumber(char chinese) {
|
||
if (20004 == chinese) {
|
||
chinese = 20108;
|
||
}
|
||
|
||
int i = indexOf(DIGITS, chinese);
|
||
return i > 0 ? (i + 1) / 2 : i;
|
||
}
|
||
|
||
/**
|
||
* 返回数组中指定元素所在位置,未找到返回-1
|
||
*
|
||
* @param array 数组
|
||
* @param value 被检查的元素
|
||
*/
|
||
public static int indexOf(char[] array, char value) {
|
||
if (null != array) {
|
||
for (int i = 0; i < array.length; i++) {
|
||
if (value == array[i]) {
|
||
return i;
|
||
}
|
||
}
|
||
}
|
||
return -1;
|
||
}
|
||
|
||
/**
|
||
* 阿拉伯数字转换成中文,小数点后四舍五入保留两位. 使用于整数、小数的转换.
|
||
*
|
||
* @param amount 数字
|
||
* @param isUseTraditional 是否使用繁体
|
||
* @param isMoneyMode 是否为金额模式
|
||
*/
|
||
public static String format(double amount, boolean isUseTraditional, boolean isMoneyMode) {
|
||
return format(amount, isUseTraditional, isMoneyMode, "负", "元");
|
||
}
|
||
|
||
/**
|
||
* 阿拉伯数字转换成中文.
|
||
* @param amount 数字
|
||
* @param isUseTraditional 是否使用繁体
|
||
* @param isMoneyMode 是否金额模式
|
||
* @param negativeName 负号转换名称 如:负、(负数)
|
||
* @param unitName 单位名称 如:元、圆
|
||
*/
|
||
private static String format(double amount, boolean isUseTraditional, boolean isMoneyMode, String negativeName,
|
||
String unitName) {
|
||
if (0 == amount) {
|
||
return "零";
|
||
}
|
||
final StringBuilder chineseStr = new StringBuilder();
|
||
|
||
// 负数
|
||
if (amount < 0) {
|
||
chineseStr.append(isNullOrUndefined(negativeName) ? "负" : negativeName);
|
||
amount = -amount;
|
||
}
|
||
|
||
long yuan = Math.round(amount * 100);
|
||
final int fen = (int) (yuan % 10);
|
||
yuan = yuan / 10;
|
||
final int jiao = (int) (yuan % 10);
|
||
yuan = yuan / 10;
|
||
|
||
// 元
|
||
if (false == isMoneyMode || 0 != yuan) {
|
||
// 金额模式下,无需“零元”
|
||
chineseStr.append(longToChinese(yuan, isUseTraditional));
|
||
if (isMoneyMode) {
|
||
chineseStr.append(isNullOrUndefined(unitName) ? "元" : unitName);
|
||
}
|
||
}
|
||
|
||
if (0 == jiao && 0 == fen) {
|
||
//无小数部分的金额结尾
|
||
if (isMoneyMode) {
|
||
chineseStr.append("整");
|
||
}
|
||
return chineseStr.toString();
|
||
}
|
||
|
||
// 小数部分
|
||
if (false == isMoneyMode) {
|
||
chineseStr.append("点");
|
||
}
|
||
|
||
// 角
|
||
if (0 == yuan && 0 == jiao) {
|
||
// 元和角都为0时,只有非金额模式下补“零”
|
||
if (false == isMoneyMode) {
|
||
chineseStr.append("零");
|
||
}
|
||
} else {
|
||
chineseStr.append(numberToChinese(jiao, isUseTraditional));
|
||
if (isMoneyMode && 0 != jiao) {
|
||
chineseStr.append("角");
|
||
}
|
||
}
|
||
|
||
// 分
|
||
if (0 != fen) {
|
||
chineseStr.append(numberToChinese(fen, isUseTraditional));
|
||
if (isMoneyMode) {
|
||
chineseStr.append("分");
|
||
}
|
||
}
|
||
|
||
return chineseStr.toString();
|
||
}
|
||
|
||
/**
|
||
* 单个数字转汉字
|
||
* @param number 数字
|
||
* @param isUseTraditional 是否使用繁体
|
||
*/
|
||
private static char numberToChinese(int number, boolean isUseTraditional) {
|
||
if (0 == number) {
|
||
return DIGITS[0];
|
||
}
|
||
return DIGITS[number * 2 - (isUseTraditional ? 0 : 1)];
|
||
}
|
||
|
||
/**
|
||
* 阿拉伯数字整数部分转换成中文,只支持正数
|
||
* @param amount 数字
|
||
* @param isUseTraditional 是否使用繁体
|
||
*/
|
||
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 (StringUtils.isNotEmpty(chineseStr) && '零' == chineseStr.charAt(0)) {
|
||
return chineseStr.substring(1);
|
||
}
|
||
|
||
return chineseStr.toString();
|
||
}
|
||
|
||
private static void addPreZero(StringBuilder chineseStr) {
|
||
if (StringUtils.isEmpty(chineseStr)) {
|
||
return;
|
||
}
|
||
final char c = chineseStr.charAt(0);
|
||
if ('零' != c) {
|
||
chineseStr.insert(0, '零');
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 把一个 0~9999 之间的整数转换为汉字的字符串,如果是 0 则返回 ""
|
||
*
|
||
* @param amountPart 数字部分
|
||
* @param isUseTraditional 是否使用繁体单位
|
||
*/
|
||
private static String thousandToChinese(int amountPart, boolean isUseTraditional) {
|
||
if (amountPart == 0) {
|
||
return String.valueOf(DIGITS[0]);
|
||
}
|
||
|
||
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();
|
||
}
|
||
|
||
/**
|
||
* 获取对应级别的单位
|
||
*
|
||
* @param index 级别,0表示各位,1表示十位,2表示百位,以此类推
|
||
* @param isUseTraditional 是否使用繁体
|
||
*/
|
||
private static String getUnitName(int index, boolean isUseTraditional) {
|
||
if (0 == index) {
|
||
return "";
|
||
}
|
||
return String.valueOf(CHINESE_NAME_VALUE[index * 2 - (isUseTraditional ? 0 : 1)].name);
|
||
}
|
||
|
||
public static boolean isNullOrUndefined(CharSequence str) {
|
||
if (null == str) {
|
||
return true;
|
||
}
|
||
return isNullOrUndefinedStr(str);
|
||
}
|
||
|
||
private static boolean isNullOrUndefinedStr(CharSequence str) {
|
||
String strString = str.toString().trim();
|
||
return NULL.equals(strString) || "undefined".equals(strString);
|
||
}
|
||
|
||
}
|