| | |
| | | package com.ruoyi.common.utils; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.util.HashMap; |
| | | import java.util.Map; |
| | | |
| | | public class ChineseUtils { |
| | | private static final String[] CN_NUM = { |
| | | "零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖" |
| | | }; |
| | | |
| | | private static final String[] CN_UNIT = { |
| | | "", "拾", "佰", "仟" |
| | | }; |
| | | |
| | | private static final String[] CN_BIG_UNIT = { |
| | | "", "万", "亿", "兆" |
| | | }; |
| | | private static final Map<Character, Integer> CN_NUM_MAP = new HashMap<>(); |
| | | private static final Map<Character, Integer> CN_UNIT_MAP = new HashMap<>(); |
| | | |
| | | static { |
| | | for (int i = 0; i < CN_NUM.length; i++) { |
| | | CN_NUM_MAP.put(CN_NUM[i].charAt(0), i); |
| | | } |
| | | CN_NUM_MAP.put('零', 0); |
| | | CN_NUM_MAP.put('一', 1); |
| | | CN_NUM_MAP.put('二', 2); |
| | | CN_NUM_MAP.put('三', 3); |
| | | CN_NUM_MAP.put('四', 4); |
| | | CN_NUM_MAP.put('五', 5); |
| | | CN_NUM_MAP.put('六', 6); |
| | | CN_NUM_MAP.put('七', 7); |
| | | CN_NUM_MAP.put('八', 8); |
| | | CN_NUM_MAP.put('九', 9); |
| | | CN_NUM_MAP.put('两', 2); |
| | | |
| | | CN_UNIT_MAP.put('拾', 10); |
| | | CN_UNIT_MAP.put('佰', 100); |
| | | CN_UNIT_MAP.put('仟', 1000); |
| | | CN_UNIT_MAP.put('十', 10); |
| | | CN_UNIT_MAP.put('百', 100); |
| | | CN_UNIT_MAP.put('千', 1000); |
| | | } |
| | | |
| | | /** |
| | | * 校验是否是中文 |
| | |
| | | * @return |
| | | */ |
| | | public static boolean isChinese(String str) { |
| | | String regex = "[\\u4e00-\\u9fa5]+"; |
| | | return str.matches(regex); |
| | | for (int i = 0; i < str.length(); i++) { |
| | | char c = str.charAt(i); |
| | | if (isChineseCharacter(c)) { |
| | | return true; |
| | | } |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | private static boolean isChineseCharacter(char c) { |
| | | // 汉字的Unicode编码范围是0x4E00到0x9FA5 |
| | | return c >= 0x4E00 && c <= 0x9FA5; |
| | | } |
| | | |
| | | /** |
| | | * 数字金额转中文大写 |
| | | * 例如:1234.56 -> 壹仟贰佰叁拾肆元伍角陆分 |
| | | */ |
| | | public static String numberToChinese(BigDecimal amount) { |
| | | if (amount == null) { |
| | | throw new IllegalArgumentException("金额不能为空"); |
| | | } |
| | | |
| | | boolean negative = amount.signum() < 0; |
| | | amount = amount.abs().setScale(2, BigDecimal.ROUND_HALF_UP); |
| | | |
| | | long integerPart = amount.longValue(); |
| | | int decimalPart = amount.subtract(new BigDecimal(integerPart)) |
| | | .multiply(new BigDecimal(100)) |
| | | .intValue(); |
| | | |
| | | int jiao = decimalPart / 10; |
| | | int fen = decimalPart % 10; |
| | | |
| | | StringBuilder sb = new StringBuilder(); |
| | | |
| | | if (negative) { |
| | | sb.append("负"); |
| | | } |
| | | |
| | | if (integerPart == 0) { |
| | | sb.append("零元"); |
| | | } else { |
| | | sb.append(integerToChinese(integerPart)).append("元"); |
| | | } |
| | | |
| | | if (jiao == 0 && fen == 0) { |
| | | sb.append("整"); |
| | | } else { |
| | | if (jiao > 0) { |
| | | sb.append(CN_NUM[jiao]).append("角"); |
| | | } else if (fen > 0 && integerPart > 0) { |
| | | sb.append("零"); |
| | | } |
| | | |
| | | if (fen > 0) { |
| | | sb.append(CN_NUM[fen]).append("分"); |
| | | } |
| | | } |
| | | |
| | | return sb.toString(); |
| | | } |
| | | |
| | | public static String numberToChinese(double amount) { |
| | | return numberToChinese(BigDecimal.valueOf(amount)); |
| | | } |
| | | |
| | | public static String numberToChinese(String amount) { |
| | | return numberToChinese(new BigDecimal(amount)); |
| | | } |
| | | |
| | | /** |
| | | * 整数部分转中文大写 |
| | | */ |
| | | private static String integerToChinese(long num) { |
| | | if (num == 0) { |
| | | return "零"; |
| | | } |
| | | |
| | | StringBuilder sb = new StringBuilder(); |
| | | int unitIndex = 0; |
| | | boolean needZero = false; |
| | | |
| | | while (num > 0) { |
| | | int section = (int) (num % 10000); |
| | | if (section != 0) { |
| | | String sectionStr = sectionToChinese(section); |
| | | if (needZero || (section < 1000 && sb.length() > 0)) { |
| | | sb.insert(0, "零"); |
| | | } |
| | | sb.insert(0, sectionStr + CN_BIG_UNIT[unitIndex]); |
| | | needZero = section < 1000; |
| | | } else { |
| | | if (sb.length() > 0 && !sb.toString().startsWith("零")) { |
| | | needZero = true; |
| | | } |
| | | } |
| | | num /= 10000; |
| | | unitIndex++; |
| | | } |
| | | |
| | | String result = sb.toString(); |
| | | result = result.replaceAll("零+", "零"); |
| | | result = result.replaceAll("零$", ""); |
| | | result = result.replaceAll("零万", "万"); |
| | | result = result.replaceAll("零亿", "亿"); |
| | | return result; |
| | | } |
| | | |
| | | /** |
| | | * 四位以内数字转中文 |
| | | */ |
| | | private static String sectionToChinese(int section) { |
| | | StringBuilder sb = new StringBuilder(); |
| | | int unit = 1000; |
| | | boolean zeroFlag = false; |
| | | |
| | | while (section > 0) { |
| | | int digit = section / unit; |
| | | if (digit == 0) { |
| | | if (sb.length() > 0 && !zeroFlag) { |
| | | zeroFlag = true; |
| | | sb.append("零"); |
| | | } |
| | | } else { |
| | | zeroFlag = false; |
| | | sb.append(CN_NUM[digit]).append(CN_UNIT[getUnitIndex(unit)]); |
| | | } |
| | | section %= unit; |
| | | unit /= 10; |
| | | } |
| | | |
| | | String result = sb.toString(); |
| | | if (result.endsWith("零")) { |
| | | result = result.substring(0, result.length() - 1); |
| | | } |
| | | return result; |
| | | } |
| | | |
| | | private static int getUnitIndex(int unit) { |
| | | switch (unit) { |
| | | case 1000: return 3; |
| | | case 100: return 2; |
| | | case 10: return 1; |
| | | default: return 0; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 中文大写金额转数字 |
| | | * 例如:壹仟贰佰叁拾肆元伍角陆分 -> 1234.56 |
| | | */ |
| | | public static BigDecimal chineseToNumber(String chinese) { |
| | | if (chinese == null || chinese.trim().isEmpty()) { |
| | | throw new IllegalArgumentException("中文金额不能为空"); |
| | | } |
| | | |
| | | String str = chinese.trim() |
| | | .replace("整", "") |
| | | .replace("正", "") |
| | | .replace("人民币", ""); |
| | | |
| | | boolean negative = str.startsWith("负"); |
| | | if (negative) { |
| | | str = str.substring(1); |
| | | } |
| | | |
| | | // 分离元、角、分 |
| | | String integerPartStr; |
| | | String decimalPartStr = ""; |
| | | |
| | | int yuanIndex = str.indexOf("元"); |
| | | if (yuanIndex >= 0) { |
| | | integerPartStr = str.substring(0, yuanIndex); |
| | | String rest = str.substring(yuanIndex + 1); |
| | | |
| | | int jiaoIndex = rest.indexOf("角"); |
| | | int fenIndex = rest.indexOf("分"); |
| | | |
| | | if (jiaoIndex >= 0) { |
| | | decimalPartStr += chineseDigitToNumber(rest.substring(0, jiaoIndex)) + ""; |
| | | } else if (fenIndex > 0) { |
| | | decimalPartStr += "0"; |
| | | } |
| | | |
| | | if (fenIndex >= 0) { |
| | | String fenStr = rest.substring(jiaoIndex >= 0 ? jiaoIndex + 1 : 0, fenIndex); |
| | | decimalPartStr += chineseDigitToNumber(fenStr) + ""; |
| | | } else { |
| | | if (decimalPartStr.length() == 1) { |
| | | decimalPartStr += "0"; |
| | | } |
| | | } |
| | | } else { |
| | | // 没有“元”,按纯数字中文处理 |
| | | integerPartStr = str; |
| | | } |
| | | |
| | | long integerPart = chineseIntegerToNumber(integerPartStr); |
| | | |
| | | if (decimalPartStr.isEmpty()) { |
| | | decimalPartStr = "00"; |
| | | } else if (decimalPartStr.length() == 1) { |
| | | decimalPartStr += "0"; |
| | | } |
| | | |
| | | BigDecimal result = new BigDecimal(integerPart + "." + decimalPartStr); |
| | | return negative ? result.negate() : result; |
| | | } |
| | | /** |
| | | * 中文整数转 long |
| | | */ |
| | | private static long chineseIntegerToNumber(String chinese) { |
| | | if (chinese == null || chinese.isEmpty() || chinese.equals("零")) { |
| | | return 0; |
| | | } |
| | | |
| | | long result = 0; |
| | | long section = 0; |
| | | long number = 0; |
| | | |
| | | for (int i = 0; i < chinese.length(); i++) { |
| | | char c = chinese.charAt(i); |
| | | |
| | | if (CN_NUM_MAP.containsKey(c)) { |
| | | number = CN_NUM_MAP.get(c); |
| | | } else if (CN_UNIT_MAP.containsKey(c)) { |
| | | int unit = CN_UNIT_MAP.get(c); |
| | | if (number == 0) { |
| | | number = 1; |
| | | } |
| | | section += number * unit; |
| | | number = 0; |
| | | } else if (c == '万') { |
| | | section += number; |
| | | result += section * 10000; |
| | | section = 0; |
| | | number = 0; |
| | | } else if (c == '亿') { |
| | | section += number; |
| | | result += section * 100000000; |
| | | section = 0; |
| | | number = 0; |
| | | } else if (c == '兆') { |
| | | section += number; |
| | | result += section * 1000000000000L; |
| | | section = 0; |
| | | number = 0; |
| | | } |
| | | } |
| | | |
| | | return result + section + number; |
| | | } |
| | | |
| | | private static int chineseDigitToNumber(String chinese) { |
| | | if (chinese == null || chinese.isEmpty()) { |
| | | return 0; |
| | | } |
| | | char c = chinese.charAt(0); |
| | | return CN_NUM_MAP.getOrDefault(c, 0); |
| | | } |
| | | } |