Skip to content

Instantly share code, notes, and snippets.

@zhanghuabin
Last active May 15, 2017 05:04
Show Gist options
  • Save zhanghuabin/342ad10e561ec4f192c6 to your computer and use it in GitHub Desktop.
Save zhanghuabin/342ad10e561ec4f192c6 to your computer and use it in GitHub Desktop.
Convert number to Chinese financial number 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