title: 如何实现整数转汉字
date: 2021-05-20
description: 讲一下如何实现整数转汉字
开始
开发中,可能会有这样的需求,将数字转换成为汉字用来展示,大概如下
let tests = [
[1, '一'],
[10, '十'],
[11, '十一'],
[20, '二十'],
[21, '二十一'],
[50, '五十'],
[55, '五十五'],
[100, '一百'],
[101, '一百零一'],
[110, '一百一十'],
[121, '一百二十'],
[200, '二百'],
[207, '二百零七'],
[1000, '一千'],
[1001, '一千零一'],
[1207, '一千二百零七'],
[10000, '一万'],
[10207, '一万零二百零七']
];
接下来我来说下我的实现思路
如何转换
观察上面的tests
数组我们可以发现一些规律
1、1位数字,也就是单个数字这种情况的话,我们直接就可以知道对应的关系,[1->一,2->二]
2、2位以上的数字都有相关的单位,十、百、千、万之类的
3、汉字组合规律
3-1、大于10小于20之类的数字都是,十、十一、十二之类的
3-2、大于20之类的数字有二十一、五十五、一百五十五,他们都是汉字加上自己的单位
3-3、有的情况还有零,比如一百零七、一千二百零七之类的
以上就是他们的一些规律,根据这些规律我们来思考下代码如何实现
实现思路
根据规律1我们可以得到下面的数值映射关系
const rules = { 0: '零', 1: '一', 2: '二', 3: '三', 4: '四', 5: '五', 6: '六', 7: '七', 8: '八', 9: '九' }
根据规律2我们可以得到下面的单位映射关系(ps:目前只处理到万,需要更大的数字的,自己扩展)
const units = {10: '十', 100: '百', 1000: '千', 10000: '万'}
上面的两个是基础数据,我们的代码将会根据这些数据构建出对应的代码
先把代码架子构建好
function num2Text(num){
const rules = { 0: '零', 1: '一', 2: '二', 3: '三', 4: '四', 5: '五', 6: '六', 7: '七', 8: '八', 9: '九' }
const units = {10: '十', 100: '百', 1000: '千', 10000: '万'}
// 最后的返回值
let res = ''
// 处理的最后一位数字
let singleNum = 0
// 上一次处理的最后一位数字
let prevSingleNum = -1
// 当前正在处理的位数
let unitNum = 1
// 对原始值进行一个隔离
let _num = num
// 处理之后得到的汉字数组
let numTexts = []
}
接下来我们可以利用循环不停的去处理各个位数,获得对应的组合规律
function num2Text(num){
//把第一步的初始数据放在这里
//...
while(_num){
// 这里利用取余获得每次处理最后一位数字
singleNum = _num % 10
// 这里根据位数获得单位,并追加上
if (units[unitNum]) {
numTexts.unshift(units[unitNum])
}
numTexts.unshift(texts[singleNum])
// 这里处理完之后将会增加位数
unitNum *= 10
// 利用向下取整获得接下来需要处理的新的数值
_num = Math.floor(_num / 10)
// 记录下本次处理的最后一位数字
prevSingleNum = singleNum
}
res = numTexts.join('')
return res
}
运行上面的代码,获得如下结果
对比组合规律,我们一步一步调整
1、首先,当位数为1位的时候我们发现没有任何问题,那就跳过
2、2位数的时候发现结果不对,应该是[一十零->十,一十一->十一,一百零->一百,一百一十零->一百一十]
先处理2,我们再说其他不对的
不难看出,前面的一
和结尾的零
是不应该出现的,针对这种情况,我们加下if
语句拦一下
更新num2Text
函数中的while
循环部分,其他的保持不变
function num2Text(num){
//保持不变...
while(_num){
// 这里利用取余获得每次处理最后一位数字
singleNum = _num % 10
// 这里根据位数获得单位,并追加上
if (units[unitNum]) {
numTexts.unshift(units[unitNum])
}
//unitNum === 1 && singleNum !== 0
//表示当处理的位数是个位并且当前处理的值不为0的时候才处理
//这样可以解决[一十零->一十,一百零->一百,一百一十零->一百一十]
//unitNum !== 1 && num >= 20
//表示当处理的位数是不是个位并且原始值大于等于20的情况
//这样可以解决[一十->十,一十一->十一]
if((unitNum === 1 && singleNum !== 0) || (unitNum !== 1 && num >= 20)){
numTexts.unshift(texts[singleNum])
}
// 这里处理完之后将会增加位数
unitNum *= 10
// 利用向下取整获得接下来需要处理的新的数值
_num = Math.floor(_num / 10)
// 记录下本次处理的最后一位数字
prevSingleNum = singleNum
}
//保持不变...
}
运行上面的代码,获得如下结果
对比组合规律,不错,这里基本上都处理完了异常情况,但是,还有一种情况
[一千零百零十->一千,一千零百零十一->一千零一, 一万零千零百零十->一万]
这里是有连续的0
出现的时候会出现这种情况,这样的情况,我们也加if
拦截一下
更新num2Text
函数中的while
循环部分,其他的保持不变
function num2Text(num){
//保持不变...
while(_num){
// 保持不变...
// 这里根据位数获得单位,并追加上
// 这里判断下当前处理的值是否为0,0的话不处理
// 这样可以解决[一千零百零十->一千零零,一千零百零十一->一千零零一, 一万零千零百零十->一万零零]
if (units[unitNum] && singleNum !== 0) {
numTexts.unshift(units[unitNum])
}
if((unitNum === 1 && singleNum !== 0) || (unitNum !== 1 && num >= 20)){
// 这里标记一下,比对一下上一次处理的值和当前需要处理的值
// 两者都为0的时候我们是不处理的,所以这里取了个反
// 这样可以解决[一千零零->一千,一千零零一->一千零一, 一万零零->一万]
if (!(prevSingleNum === 0 && singleNum === 0)) {
numTexts.unshift(texts[singleNum])
}
}
// 保持不变...
}
//保持不变...
}
运行上面的代码,获得如下结果
对比组合规律,完美,结果明显,至此,我们就完成一个整数转汉子的方法,其实还有其他方式
1、递归代替循环处理
2、将数字转换成字符串,字符串拆分成数组一个个处理
...
这里只处理到了万,更大的位数怎么扩展和处理就由自己自行扩展了
下面是上述的完整函数
function num2Text(num){
const rules = { 0: '零', 1: '一', 2: '二', 3: '三', 4: '四', 5: '五', 6: '六', 7: '七', 8: '八', 9: '九' }
const units = {10: '十', 100: '百', 1000: '千', 10000: '万'}
// 最后的返回值
let res = ''
// 处理的最后一位数字
let singleNum = 0
// 上一次处理的最后一位数字
let prevSingleNum = -1
// 当前正在处理的位数
let unitNum = 1
// 对原始值进行一个隔离
let _num = num
// 处理之后得到的汉字数组
let numTexts = []
while(_num){
// 这里利用取余获得每次处理最后一位数字
singleNum = _num % 10
// 这里根据位数获得单位,并追加上
// 这里判断下当前处理的值是否为0,0的话不处理
// 这样可以解决[一千零百零十->一千零零,一千零百零十一->一千零零一, 一万零千零百零十->一万零零]
if (units[unitNum] && singleNum !== 0) {
numTexts.unshift(units[unitNum])
}
//unitNum === 1 && singleNum !== 0
//表示当处理的位数是个位并且当前处理的值不为0的时候才处理
//这样可以解决[一十零->一十,一百零->一百,一百一十零->一百一十]
//unitNum !== 1 && num >= 20
//表示当处理的位数是不是个位并且原始值大于等于20的情况
//这样可以解决[一十->十,一十一->十一]
if((unitNum === 1 && singleNum !== 0) || (unitNum !== 1 && num >= 20)){
// 这里标记一下,比对一下上一次处理的值和当前需要处理的值
// 两者都为0的时候我们是不处理的,所以这里取了个反
// 这样可以解决[一千零零->一千,一千零零一->一千零一, 一万零零->一万]
if (!(prevSingleNum === 0 && singleNum === 0)) {
numTexts.unshift(texts[singleNum])
}
}
// 这里处理完之后将会增加位数
unitNum *= 10
// 利用向下取整获得接下来需要处理的新的数值
_num = Math.floor(_num / 10)
// 记录下本次处理的最后一位数字
prevSingleNum = singleNum
}
res = numTexts.join('')
return res
}
//跑下测试
let tests = [
[1, '一'],
[10, '十'],
[11, '十一'],
[20, '二十'],
[21, '二十一'],
[50, '五十'],
[55, '五十五'],
[100, '一百'],
[101, '一百零一'],
[110, '一百一十'],
[121, '一百二十'],
[200, '二百'],
[207, '二百零七'],
[1000, '一千'],
[1001, '一千零一'],
[1207, '一千二百零七'],
[10000, '一万'],
[10207, '一万零二百零七']
]
for (let i = 0; i < tests.length; i++) {
const cur = tests[i]
console.log('ask-num2Text', cur, num2Text(cur[0]))
}
用for
+递归
循环实现的版本
num2TextFor (num) {
const texts = { 0: '零', 1: '一', 2: '二', 3: '三', 4: '四', 5: '五', 6: '六', 7: '七', 8: '八', 9: '九' }
const units = ['十', '百', '千', '万']
let res = ''
if (num < 10) {
res = texts[num] || ''
} else {
let numSources = (num + '').split('').reverse()
let _length = numSources.length
let numTexts = []
for (let i = 0; i < _length; i++) {
const cur = Number(numSources[i])
// console.log('ask-cur', cur, i, _length)
if (i >= 1 && cur !== 0) {
numTexts.unshift(units[i - 1])
}
if (i === 0 && cur === 0) {
continue
}
if (cur === 0 && Number(numSources[i - 1]) === 0) {
continue
}
if (i === 1 && num < 20) {
continue
}
numTexts.unshift(num2TextFor(cur))
// console.log('ask-curNumTexts', numTexts.toString())
}
res = numTexts.join('')
}
return res
}