Last active
May 15, 2017 05:04
-
-
Save zhanghuabin/342ad10e561ec4f192c6 to your computer and use it in GitHub Desktop.
Convert number to Chinese financial number characters, 将数字转换为中文财务数字字符
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.text.NumberFormat | |
/** | |
* 数字工具集 | |
*/ | |
class NumberUtils extends org.apache.commons.lang3.math.NumberUtils { | |
private static final _NUMBER_FORMAT = NumberFormat.numberInstance | |
//<editor-fold defaultstate="collapsed" desc="各尺度下的数字与中文映射关系"> | |
private static final _FINANCIAL_NUMBER_CHS_DIGIT_CHARS = [ | |
'零', | |
'壹', | |
'贰', | |
'叁', | |
'肆', | |
'伍', | |
'陆', | |
'柒', | |
'捌', | |
'玖' | |
] | |
private static final _FINANCIAL_NUMBER_LARGE_SCALE_INTEGRAL_PART_UNIT_CHAR_MAPPINGS = [ | |
[1000000000000, '兆'], | |
[100000000, '亿'], | |
[10000, '万'], | |
[1, '元'] | |
] | |
private static final _FINANCIAL_NUMBER_STEP_SCALE_INTEGRAL_PART_UNIT_CHAR_MAPPINGS = [ | |
[1000, '仟'], | |
[100, '佰'], | |
[10, '拾'], | |
[1, ''] | |
] | |
private static final _FINANCIAL_NUMBER_LARGE_SCALE_FLOAT_PART_UNIT_CHAR_MAPPINGS = [ | |
[100, ''], | |
[1, ''] | |
] | |
private static final _FINANCIAL_NUMBER_STEP_SCALE_FLOAT_PART_UNIT_CHAR_MAPPINGS = [ | |
[10, '角'], | |
[1, '分'] | |
] | |
private static final _FINANCIAL_NUMBER_MAX_VALUE = 10000000000000000 // 万兆 | |
private static final _FINANCIAL_NUMBER_CHS_PREFIX_NEGATIVE = '负' | |
private static final _FINANCIAL_NUMBER_CHS_SUFFIX_NO_FLOAT_PART = '整' | |
// 零元整 | |
private static final _FINANCIAL_NUMBER_CHS_ZERO = "${_FINANCIAL_NUMBER_CHS_DIGIT_CHARS[0]}${_FINANCIAL_NUMBER_LARGE_SCALE_INTEGRAL_PART_UNIT_CHAR_MAPPINGS[-1][-1]}$_FINANCIAL_NUMBER_CHS_SUFFIX_NO_FLOAT_PART" | |
// 无整数部时浮点部的整数部占位符 | |
private static final _FINANCIAL_NUMBER_FLOAT_PART_PLACE_HOLDER_NO_INTEGRAL_PART = '$$$' | |
//</editor-fold> | |
//<editor-fold defaultstate="collapsed" desc="核心计算单元"> | |
private static final _GET_DIGIT_INFO_BASE = { num, prev, curr -> | |
def (prevQuotient, _, prevRemainder) = prev ? prev[-1] : prev | |
def (amount, chsUnitChar) = curr | |
def dividend = null != prevRemainder ? prevRemainder : num | |
def (currQuotient, currRemainder) = dividend.divideAndRemainder(amount) | |
if (currQuotient || prevQuotient) { | |
prev << [currQuotient, chsUnitChar, currRemainder, amount] | |
} else { | |
prev | |
} | |
} | |
private static final _GET_DIGIT_PLACE_HOLDER_ANY_SCALE_BASE = { unitCharMappings, prev, curr -> | |
def (quotient) = curr | |
(quotient / unitCharMappings[0][0]) as long ? | |
'' | |
: prev && quotient ? | |
_FINANCIAL_NUMBER_CHS_DIGIT_CHARS[0] : '' | |
} | |
private static final _GET_CHS_TEXT_BASE = { getDigitPlaceHolder, getUnitChar, getStepScaleChsText, prev, curr -> | |
def (quotient) = curr | |
def digitPlaceHolder = getDigitPlaceHolder prev, curr | |
def unitChar = getUnitChar prev, curr | |
def stepScaleChsText = getStepScaleChsText quotient | |
"$prev$digitPlaceHolder$stepScaleChsText$unitChar" | |
} | |
//</editor-fold> | |
//<editor-fold defaultstate="collapsed" desc="整数部"> | |
private static final _GET_DIGIT_PLACE_HOLDER_LARGE_SCALE_INTEGRAL_PART = _GET_DIGIT_PLACE_HOLDER_ANY_SCALE_BASE.curry _FINANCIAL_NUMBER_STEP_SCALE_INTEGRAL_PART_UNIT_CHAR_MAPPINGS | |
private static final _GET_UNIT_CHAR_LARGE_SCALE_INTEGRAL_PART = { prev, curr -> | |
def (quotient, chsUnitChar, _, amount) = curr | |
quotient || (_FINANCIAL_NUMBER_LARGE_SCALE_INTEGRAL_PART_UNIT_CHAR_MAPPINGS[-1][0] == amount) ? | |
chsUnitChar : '' | |
} | |
private static final _GET_STEP_SCALE_INTEGRAL_PART_CHS_TEXT = { num -> | |
getIntegerChsText num, _FINANCIAL_NUMBER_STEP_SCALE_INTEGRAL_PART_UNIT_CHAR_MAPPINGS | |
} | |
private static final _GET_CHS_TEXT_LARGE_SCALE_INTEGRAL_PART = _GET_CHS_TEXT_BASE.curry(_GET_DIGIT_PLACE_HOLDER_LARGE_SCALE_INTEGRAL_PART, _GET_UNIT_CHAR_LARGE_SCALE_INTEGRAL_PART, _GET_STEP_SCALE_INTEGRAL_PART_CHS_TEXT) | |
//</editor-fold> | |
//<editor-fold defaultstate="collapsed" desc="浮点部"> | |
private static final _GET_DIGIT_PLACE_HOLDER_LARGE_SCALE_FLOAT_PART = _GET_DIGIT_PLACE_HOLDER_ANY_SCALE_BASE.curry _FINANCIAL_NUMBER_STEP_SCALE_FLOAT_PART_UNIT_CHAR_MAPPINGS | |
private static final _GET_UNIT_CHAR_LARGE_SCALE_FLOAT_PART = { prev, curr -> | |
def (quotient, chsUnitChar) = curr | |
quotient ? chsUnitChar : '' | |
} | |
private static final _GET_STEP_SCALE_FLOAT_PART_CHS_TEXT = { num -> | |
getIntegerChsText num, _FINANCIAL_NUMBER_STEP_SCALE_FLOAT_PART_UNIT_CHAR_MAPPINGS | |
} | |
private static final _GET_CHS_TEXT_LARGE_SCALE_FLOAT_PART = _GET_CHS_TEXT_BASE.curry(_GET_DIGIT_PLACE_HOLDER_LARGE_SCALE_FLOAT_PART, _GET_UNIT_CHAR_LARGE_SCALE_FLOAT_PART, _GET_STEP_SCALE_FLOAT_PART_CHS_TEXT) | |
//</editor-fold> | |
//<editor-fold defaultstate="collapsed" desc="生成财务数字中文文本"> | |
/** | |
* 生成财务数字中文文本 | |
* @param num | |
* @return | |
*/ | |
static String toFinancialNumberChsText(num) { | |
//<editor-fold defaultstate="collapsed" desc="数据合法性校验"> | |
if (null == num || '' == num) { | |
return '' | |
} | |
try { | |
num = num as BigDecimal | |
} catch (ex) { | |
return '' | |
} | |
if (0 == num) { | |
return _FINANCIAL_NUMBER_CHS_ZERO | |
} | |
def positive = (0 < num).with { | |
if (!it) { | |
num = -num | |
} | |
it | |
} | |
if (_FINANCIAL_NUMBER_MAX_VALUE <= num) { | |
return '' | |
} | |
//</editor-fold> | |
//<editor-fold defaultstate="collapsed" desc="整数与浮点部分别计算,最后合并在一起"> | |
def (intPart, floatPart) = (num * 100).divideAndRemainder(100) | |
def intPartText = getLargeScaleIntegralPartChsText intPart | |
def floatPartText = getLargeScaleFloatPartChsText floatPart, intPartText | |
"${positive ? '' : _FINANCIAL_NUMBER_CHS_PREFIX_NEGATIVE}$intPartText${floatPartText ?: _FINANCIAL_NUMBER_CHS_SUFFIX_NO_FLOAT_PART}" | |
//</editor-fold> | |
} | |
private static getLargeScaleIntegralPartChsText(num) { | |
_FINANCIAL_NUMBER_LARGE_SCALE_INTEGRAL_PART_UNIT_CHAR_MAPPINGS.inject([], _GET_DIGIT_INFO_BASE.curry(num)).inject('', _GET_CHS_TEXT_LARGE_SCALE_INTEGRAL_PART) | |
} | |
private static getLargeScaleFloatPartChsText(num, intPartText) { | |
_FINANCIAL_NUMBER_LARGE_SCALE_FLOAT_PART_UNIT_CHAR_MAPPINGS.inject([], _GET_DIGIT_INFO_BASE.curry(num)).inject(intPartText ? _FINANCIAL_NUMBER_FLOAT_PART_PLACE_HOLDER_NO_INTEGRAL_PART : '', _GET_CHS_TEXT_LARGE_SCALE_FLOAT_PART) - _FINANCIAL_NUMBER_FLOAT_PART_PLACE_HOLDER_NO_INTEGRAL_PART | |
} | |
/** | |
* 把一个整数转换为中文财务数字 | |
* @param num | |
* @param unitCharMappings | |
* @return | |
*/ | |
private static getIntegerChsText(num, unitCharMappings) { | |
unitCharMappings.inject([], _GET_DIGIT_INFO_BASE.curry(num)).reverse().inject([], { prev, curr -> | |
// 清理掉所有尾部的“0” | |
def (prevQuotient) = prev ? prev[-1] : prev | |
def (currQuotient) = curr | |
if (currQuotient || prevQuotient) { | |
prev << curr | |
} else { | |
prev | |
} | |
}).reverse().inject('', { prev, curr -> | |
def (quotient, chsUnitChar) = curr | |
"$prev${_FINANCIAL_NUMBER_CHS_DIGIT_CHARS[quotient as int]}${quotient ? chsUnitChar : ''}" | |
}) | |
} | |
//</editor-fold> | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment