源码讲解
核心思路主要可以分为以下几个步骤:
- 参数检查与初始化:
- 首先,检查传入的
num
是否为null
。如果是,则直接返回两个空字符串,表示没有数字可转换。 - 初始化一些用于存储结果和处理过程中的变量,如
plainChineseNum
(普通中文数字字符串),currencyChineseNum
(货币中文数字字符串),zeroCount
(用来计数连续零的个数),等等。
- 首先,检查传入的
- 数字转字符串与分割:
- 将数字转换为字符串形式,然后根据小数点分割成整数部分和小数部分。
- 处理整数部分:
- 从最低位(即字符串的最后一位)开始向最高位迭代,这样可以更方便地处理单位(个、十、百、千)。
- 对每个数字:
- 直接转换为对应的中文数字并加到
plainChineseNum
的前端。 - 如果数字非零,则添加相应的单位(基础单位加高位单位)到货币表示的字符串中。
- 如果数字是零,特别处理连续零的情况,避免出现多余的“零”。
- 直接转换为对应的中文数字并加到
- 处理每个四位小节:
- 每当完成一个四位数字的小节,或者到达最左边的数字时,检查并添加高位单位(如万、亿等)。
- 清理并重置用于构建的临时字符串和连续零的计数器。
- 处理小数点及小数部分(普通读法):
- 如果存在小数部分,添加“点”后,将小数部分的每一位数字转换成中文并追加到
plainChineseNum
。
- 如果存在小数部分,添加“点”后,将小数部分的每一位数字转换成中文并追加到
- 处理货币读法的小数部分:
- 对货币的小数部分进行特殊处理,第一位表示“角”,第二位表示“分”,注意只有当相应的数字非零时才添加。
- 整理和返回结果:
- 在最后的货币读法中,清理尾部多余的“零”,并根据是否有小数部分添加“圆整”或“圆”。
- 返回一个对象,包含普通的中文数字读法和货币的中文数字读法。
这种方法确保了代码可以有效地转换任意数字到其对应的中文读法,包括较为复杂的货币表示方法。整个过程考虑到了中文数字表达的特点,如单位的正确使用和连续零的合理处理。
定义数字到中文的映射
基本单位(个、十、百、千),以及高位单位(万、亿等)的数组。这些应该在函数外部定义好。
// 数字映射
const numMap = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖']
// 基本单位
const units = ['', '拾', '佰', '仟']
// 高级单位
const highUnits = ['', '万', '亿', '兆']
创建数字转人民币大写的函数。
function convertNumberToChinese(num: number | null): { plainChinese: string, currencyChinese: string } {
if (num === null) {
return {
plainChinese: '',
currencyChinese: '',
}
}
const numStr = num.toString()
const [integerPart, decimalPart] = numStr.split('.')
let plainChineseNum = ''
let currencyChineseNum = ''
let zeroCount = 0 // 连续零的计数
let currencyTemp = '' // 当前处理的小节字符串
// 处理整数部分
for (let i = integerPart.length - 1; i >= 0; i--) {
const digit = Number.parseInt(integerPart[i], 10)
const position = integerPart.length - 1 - i
const unit = units[position % 4]
const highUnit = highUnits[Math.floor(position / 4)]
// 直接转换为中文数字
plainChineseNum = numMap[digit] + plainChineseNum
if (digit !== 0) {
currencyTemp = numMap[digit] + unit + currencyTemp
zeroCount = 0 // 重置连续零计数
}
else {
zeroCount++
if (zeroCount === 1) { // 只在第一次遇到零时添加
currencyTemp = `零${currencyTemp}`
}
}
// 每完成一个小节或到达最左边一位
if (position % 4 === 3 || i === 0) {
if (zeroCount < 4) { // 如果小节中有非零数字
currencyTemp = currencyTemp + highUnit
}
currencyChineseNum = currencyTemp + currencyChineseNum
currencyTemp = ''
zeroCount = 0 // 重置连续零计数
}
}
// 添加小数点
if (decimalPart) {
plainChineseNum += '点'
for (let i = 0; i < decimalPart.length; i++) {
const digit = Number.parseInt(decimalPart[i], 10)
plainChineseNum += numMap[digit]
}
}
currencyChineseNum = currencyChineseNum.replace(/零+$/, '') // 删除结尾的连续零
currencyChineseNum += '圆'
// 处理小数部分
if (decimalPart) {
if (decimalPart[0] !== '0')
currencyChineseNum += `${numMap[Number.parseInt(decimalPart[0])]}角`
if (decimalPart.length > 1 && decimalPart[1] !== '0')
currencyChineseNum += `${numMap[Number.parseInt(decimalPart[1])]}分`
}
else {
currencyChineseNum += '整'
}
return {
plainChinese: plainChineseNum,
currencyChinese: currencyChineseNum,
}
}
创建人民币大写转数字的函数
interface NumMap {
[key: string]: number
}
interface UnitMap {
[key: string]: number
}
const chineseMapping: {
numbers: NumMap
basicUnits: UnitMap
highUnits: UnitMap
} = {
numbers: { 零: 0, 壹: 1, 贰: 2, 叁: 3, 肆: 4, 伍: 5, 陆: 6, 柒: 7, 捌: 8, 玖: 9 },
basicUnits: { 拾: 10, 佰: 100, 仟: 1000 },
highUnits: { 万: 10000, 亿: 100000000, 兆: 1000000000000 },
}
function chineseToArabic(chineseNumber: string): string {
let total = 0
let currentTotal = 0 // 用于暂存当前小节的值
let currentValue = 0 // 当前数字值
const [integerPart, decimalPart] = chineseNumber.split('圆')
// 处理整数部分
for (let i = 0; i < integerPart.length; i++) {
const char = integerPart[i]
if (chineseMapping.numbers[char] !== undefined) {
currentValue = chineseMapping.numbers[char]
}
else if (chineseMapping.basicUnits[char] !== undefined) {
currentTotal += currentValue * chineseMapping.basicUnits[char]
currentValue = 0
}
else if (chineseMapping.highUnits[char] !== undefined) {
currentTotal += currentValue
total += currentTotal * chineseMapping.highUnits[char]
currentTotal = 0 // 重置当前小节总和
currentValue = 0 // 重置当前数字值
}
}
total += currentTotal + currentValue // 加上最后的小节和剩余的数字
// 处理小数部分
let decimalResult = ''
if (decimalPart) {
let decimalValue = 0
let factor = 0.1 // "角" 是十分之一
for (let i = 0; i < decimalPart.length; i++) {
const char = decimalPart[i]
if (chineseMapping.numbers[char] !== undefined) {
decimalValue += chineseMapping.numbers[char] * factor
if (factor === 0.1) { // 第一次是"角"
factor = 0.01 // 下一次是"分"
}
}
}
decimalResult = decimalValue.toFixed(2).slice(1)
}
return total.toString() + decimalResult
}