trans(123456) —— 十二万三千四百五十六
trans(100010001)—— 一亿零一万零一
23.12.18 更新,有同学投稿提供了自己的答案,更加简洁
function NumToChina(n) {
n = n.toString();
let numbers = ["零", "一", "二", "三", "四", "五", "六", "七", "八", "九"];
if (n === "0") return numbers[0];
let units = ["", "十", "百", "千"];
let len = n.length;
let res = "";
for (let i = 0; i < len; i++) {
let num = Number(n[i]);
if (num != 0) {
if (n[i - 1] === "0") res = res + numbers[0];
res = res + numbers[num] + units[len - i - 1];
}
}
if (len == 2 && n[0] == "1") res = res.slice(1);
return res;
}
function numTo(n) {
const isLose = n < 0;
n = Math.abs(n).toString();
let res = [];
let len = n.length;
for (let i = len; i > 0; i -= 4) {
res.push(NumToChina(n.slice(Math.max(0, i - 4), i)));
}
const units = ["", "万", "亿"];
for (let i = 0; i < res.length; i++) {
if (res[i] == "") continue;
res[i] = res[i] + units[i];
}
isLose && res.push("负");
return res.reverse().join("");
}
numTo(12345);
以下是原答案:
/**
* 阿拉伯数字转中文数字,
* 如果传入数字时则最多处理到21位,超过21位js会自动将数字表示成科学计数法,导致精度丢失和处理出错
* 传入数字字符串则没有限制
* @param {number|string} digit
*/
function toZhDigit(digit) {
digit = typeof digit === 'number' ? String(digit) : digit;
const zh = ['零', '一', '二', '三', '四', '五', '六', '七', '八', '九'];
const unit = ['千', '百', '十', ''];
const quot = ['万', '亿', '兆', '京', '垓', '秭', '穰', '沟', '涧', '正', '载', '极', '恒河沙', '阿僧祗', '那由他', '不可思议', '无量', '大数'];
let breakLen = Math.ceil(digit.length / 4);
let notBreakSegment = digit.length % 4 || 4;
let segment;
let zeroFlag = [], allZeroFlag = [];
let result = '';
while (breakLen > 0) {
if (!result) { // 第一次执行
segment = digit.slice(0, notBreakSegment);
let segmentLen = segment.length;
for (let i = 0; i < segmentLen; i++) {
if (segment[i] != 0) {
if (zeroFlag.length > 0) {
result += '零' + zh[segment[i]] + unit[4 - segmentLen + i];
// 判断是否需要加上 quot 单位
if (i === segmentLen - 1 && breakLen > 1) {
result += quot[breakLen - 2];
}
zeroFlag.length = 0;
} else {
result += zh[segment[i]] + unit[4 - segmentLen + i];
if (i === segmentLen - 1 && breakLen > 1) {
result += quot[breakLen - 2];
}
}
} else {
// 处理为 0 的情形
if (segmentLen == 1) {
result += zh[segment[i]];
break;
}
zeroFlag.push(segment[i]);
continue;
}
}
} else {
segment = digit.slice(notBreakSegment, notBreakSegment + 4);
notBreakSegment += 4;
for (let j = 0; j < segment.length; j++) {
if (segment[j] != 0) {
if (zeroFlag.length > 0) {
// 第一次执行zeroFlag长度不为0,说明上一个分区最后有0待处理
if (j === 0) {
result += quot[breakLen - 1] + zh[segment[j]] + unit[j];
} else {
result += '零' + zh[segment[j]] + unit[j];
}
zeroFlag.length = 0;
} else {
result += zh[segment[j]] + unit[j];
}
// 判断是否需要加上 quot 单位
if (j === segment.length - 1 && breakLen > 1) {
result += quot[breakLen - 2];
}
} else {
// 第一次执行如果zeroFlag长度不为0, 且上一划分不全为0
if (j === 0 && zeroFlag.length > 0 && allZeroFlag.length === 0) {
result += quot[breakLen - 1];
zeroFlag.length = 0;
zeroFlag.push(segment[j]);
} else if (allZeroFlag.length > 0) {
// 执行到最后
if (breakLen == 1) {
result += '';
} else {
zeroFlag.length = 0;
}
} else {
zeroFlag.push(segment[j]);
}
if (j === segment.length - 1 && zeroFlag.length === 4 && breakLen !== 1) {
// 如果执行到末尾
if (breakLen === 1) {
allZeroFlag.length = 0;
zeroFlag.length = 0;
result += quot[breakLen - 1];
} else {
allZeroFlag.push(segment[j]);
}
}
continue;
}
}
--breakLen;
}
return result;
}
从左至右,先把数字按万分位分组,每组加上对应的单位(万,亿, …),然后每个分组进行迭代。
breakLen表示能够分成多少个分组,notBreakSegment表示当前已处理过的分组长度。
while循环中有一个if判断,如果不存在result,则说明是第一次处理,那么在处理上是有些不同的。
- 首先,在segment的赋值上,第一次是从0开始,取notBreakSegment的长度,后面每迭代一次notBreakSegment都要在上一个值上加4
- 其次,第一次处理不用判断上一个分组是否全为0的情形,这里zeroFlag表示每一个分组内存在0的个数,allZeroFalg表示当前分组前面出现的全为0的分组的个数。
- 此外,在第一次执行时,还处理了只传入为0的情形。
每次处理segment[i]时,都要先判断当前值是否为0,为0时则直接记录到zeroFlag,然后进入下一次迭代,如果不为0,首先得判断上一个数字是否为0, 然后还得根据上一个0是否位于上一个分组的末位,来添加quot,最后还需要清空标志位。如果当前分组全为0,则标记allZeroFlag,所以在下一个分组处理时,还需要判断上一个分组是否全为0。