将阿拉伯数字的价格转换为中文价格的实现

当你有涉及到财税的需求时,可能会像我一样遇到转换数字价格为中文大写价格的需求,这里提供一个算法实现的思路。

确定输入边界

我们参考税务局网站的实现(截止于本文截稿日期 2023/11/10):

  1. 当我们输入超过 13 位的数字时会提示 整数位已超过最大值。
  2. 输入 3 位小数时格式化为 0。

确定特定输入的输出值

在处理转换的过程可能会遇到一些比较特殊的输入值(或者说比较模糊),比如:

  1. 输入 0.01 输出 壹分
  2. 输入 1.01 输出 壹元零壹分
  3. 输入 0 输出 零元整

以上输出均参考税务局网站的实现

Coding 思路

首先排除边界:

ts 复制代码
function getChinesePrice(price: number) {
  if (price === 0) return '零元整';
  if (price >= 1e12) return '整数位已超过最大值';
}

第二步收集大写中文字符集:

这里 CHINESE_UNIT_MAP 与 CHINESE_BIG_UNIT_MAP 的第一位为空是为了便于 0 位取值(边界处理)。

ts 复制代码
const CHINESE_NUMBER_MAP = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖'];
const CHINESE_UNIT_MAP = ['', '拾', '佰', '仟'];
const CHINESE_BIG_UNIT_MAP = ['', '万', '亿'];
const CHINESE_SMALL_UNIT_MAP = ['角', '分', '厘'];

然后通过 split('.') 分割整数部与小数部:

ts 复制代码
const priceStr = price.toString();
const priceArr = priceStr.split('.');
const integer = priceArr[0]; // 整数部
const decimal = priceArr[1]; // 小数部

转换整数部稍微复杂一些,我们先列出需要考虑的几点:

  1. 需要处理万和亿这两种进位。
  2. 需要合并中间多余的0,比如输入 1009 应当转换成 壹仟零玖元整 而不是 壹仟零零玖元整。

综上所述,我的实现的思路如下:

  1. 壹万万 等于 壹亿,那么我们可以用 壹万(10000) 作为进位,将当前单位的位置除以 4 可得其对应的万字位 , 将当前单位取模 4 可得其对应的个 十 百 千位。
  2. 合并多余的0的实现位,只在当前位不为 0 的情况下转换字符,并跳过个、万、亿位。

代码实现:

ts 复制代码
let chineseIntegerPrice = '';
let zeroCount = 0;

for (let i = 0; i < integer.length; i++) {
  const num = +integer[i];
  const unit = integer.length - i - 1; // 当前数字的单位
  const quotient = Math.floor(unit / 4); // 1w为进位单位, 除 4 即为 万 亿
  const remainder = unit % 4; // 1w为进位单位, 取模 4 即为 个 十 百 千

  if (num === 0) {
    zeroCount++;
  } else {
    // 处理前置的零
    if (zeroCount > 0) chineseIntegerPrice += CHINESE_NUMBER_MAP[0];
    zeroCount = 0;
    chineseIntegerPrice += CHINESE_NUMBER_MAP[num] + CHINESE_UNIT_MAP[remainder];
  }
  // 跳过个、万、亿位
  if (remainder === 0 && zeroCount < 4) {
    chineseIntegerPrice += CHINESE_BIG_UNIT_MAP[quotient];
  }
}

这种方式需要额外处理价格为小数时,整数部分不显示的情况:

ts 复制代码
// 价格为小数时,整数部分不显示
if (price < 1) chineseIntegerPrice = '';
else chineseIntegerPrice += '元';

处理小数部只需要对位合并即可,需要注意的是,在税务局的实现中,输入 1.01 输出的是 壹元零壹分:

ts 复制代码
let chineseDecimalPrice = '';

if (!decimal) {
  chineseDecimalPrice = '整';
} else {
  let hasZero = false;
  for (let i = 0; i < decimal.length; i++) {
    const num = +decimal[i];
    if (num) chineseDecimalPrice += CHINESE_NUMBER_MAP[num] + CHINESE_SMALL_UNIT_MAP[i];
    else hasZero = true;
  }
  // 处理 1.01 -> 壹元零壹分 的情况
  if (chineseIntegerPrice && hasZero) chineseIntegerPrice += '零';
}

最后合并整数部与小数部即可,完整代码参考下面 👇 的 Github 实现源码。

相关链接

Github 实现源码

测试用例

JS 数字金额大写转换

相关推荐
Highcharts.js1 小时前
BI可视化图表“维护难”问题的解决方案
开发语言·前端·javascript·编辑器·ecmascript·bi·highcharts
倚窗听花_2 小时前
IIS部署前端Vue项目
前端·javascript·vue.js
yixun_gdas2 小时前
政企网站错敏巡查:错敏都藏在哪些位置?
开发语言·前端·javascript·内容运营
小羊没烦恼!2 小时前
.NET 11 Runtime Async 详解
java·运维·开发语言·javascript·python
飞飞鱼在摸鱼3 小时前
前端转Agent开发 | JavaScript 从 0 到 1 完全指南
前端·javascript
Suprman3 小时前
从 B/S 到 Electron:平面排版套料系统桌面版开发实战
javascript·平面·electron·排序算法
u0111026753 小时前
Vue 3 统一右侧编辑面板:布局、滚动与底部操作区设计
javascript·vue.js·ecmascript
change_fate3 小时前
Vue 3 Composable 函数指南
前端·javascript·vue.js
tryCbest3 小时前
Vue3——第 9 篇:组件通信全攻略(下)——Provide / Inject 与事件总线
前端·javascript·vue.js
漫步人生走在路上5 小时前
外贸SEO中的品牌词与产品词策略:大卖家和小卖家的不同打法
大数据·javascript·网络·数据库·人工智能·搜索引擎