Contents

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

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

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

确定输入边界

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

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

确定特定输入的输出值

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

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

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

Coding 思路

首先排除边界:

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

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

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

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

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

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 的情况下转换字符,并跳过个、万、亿位。

代码实现:

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];
  }
}

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

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

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

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 数字金额大写转换