js大写金额转数字

585 阅读5分钟

 1.思路:

按照亿、万、佰、元、角、分,进行分割处理。在亿、万、佰、元之前数额在进一步分割处理。如:‘叁仟肆佰伍拾亿零壹佰陆拾柒万叁仟肆佰伍拾壹元捌角肆分’ 。

第一步是:去掉‘零’和‘整’这样的汉字。

第二步是:拆分为:‘叁仟肆佰伍拾亿,壹佰陆拾柒万,叁仟肆佰伍拾壹元,捌角,肆分’。

第三步是:将‘叁仟肆佰伍拾亿’的‘叁仟肆佰伍拾’处理为数字:‘3450’;同样‘壹佰陆拾柒万’:‘167’;

‘叁仟肆佰伍拾壹’:‘3451’。

第四步:将亿前面处理好的‘3450’ * 100000000;同样‘167’10000、‘3451’1、80.1、40.01。

最后就是相加了:345001673451.84

2.js实现(输入值是stirng类型)

这里使用正则表达式进行单位金额的分割。getInfo()是第三步的处理函数,获取亿、万、佰之前的金额数值。(看到这里已经够了,对金额精度没有要求的够用)

// '叁仟肆佰伍拾亿零壹佰陆拾柒万叁仟肆佰伍拾壹元捌角伍分' => '345001673451.85'
const getMoneyNum = (money) => {
  const dic= {
    零: 0,
    壹: 1,
    贰: 2,
    叁: 3,
    肆: 4,
    伍: 5,
    陆: 6,
    柒: 7,
    捌: 8,
    玖: 9,
  };

  // 处理仟,佰,拾;'叁仟肆佰伍拾壹' => '3451'
  const getInfo = (item) => {
    let itemCount = 0;

    // 将仟,佰,拾 拆分数组 '叁仟肆佰伍拾壹' => ['叁仟', '肆佰', '伍拾', '壹']
    let arr = item
      .replace(/(\w*仟)(\w*)/, "$1,$2")
      .replace(/(\w*佰)(\w*)/, "$1,$2")
      .replace(/(\w*拾)(\w*)/, "$1,$2")
      .split(',')
      .filter(_item => {
        return _item && _item.trim();
      });

    // 处理每个单位对应的值
    for (let subItem of arr) {
      let currValue = 0;
      let subArr = subItem.split('');
      if (subArr[1] === '拾') {
        // 处理拾位
        currValue = dic[subArr[0]] * 10;
      } else if (subArr[1] === '佰') {
        //处理佰位
        currValue = dic[subArr[0]] * 100;
      } else if (subArr[1] === '仟') {
        // 处理仟位
        currValue = dic[subArr[0]] * 1000;
      } else {
        // 处理个位
        currValue = dic[subArr[0]];
      }
      itemCount += currValue;
    }
    return itemCount;
  };

  let totalMoney = 0;

  // 按照亿,万,拆分成数组;'叁仟肆佰伍拾亿零壹佰陆拾柒万叁仟肆佰伍拾壹元捌角伍分' => ['叁仟肆佰伍拾亿', '壹佰陆拾柒万', '叁仟肆佰伍拾壹元', '捌角', '伍分']
  let newMoney = money
    .replace(/零/g, '').replace(/整/g, '') // 去掉 '零','整'
    .replace(/(\w*亿)(\w*)/, "$1,$2")
    .replace(/(\w*万)(\w*)/, "$1,$2")
    .replace(/(\w*元)(\w*)/, "$1,$2")
    .replace(/(\w*角)(\w*)/, "$1,$2")
    .split(',')
    .filter(_item => {
      return _item && _item.trim();
    });

  // 按照亿,万及以下单位为组, 循环求解求解数值;
  for (let mainItem of newMoney) {
    let currMoney = 0;
    let mainArr = mainItem.split('');

    if (mainArr[mainArr.length - 1] === '亿') {
      mainArr.pop(); // 去掉亿单位
      currMoney = getInfo(mainArr.join('')) * 100000000; // 求仟,佰,拾
    } else if (mainArr[mainArr.length - 1] === '万') {
      mainArr.pop(); // 去掉万单位
      currMoney = getInfo(mainArr.join('')) * 10000; // 求仟,佰,拾
    } else if (mainArr[mainArr.length - 1] === '元') {
      mainArr.pop(); // 去掉元
      currMoney = getInfo(mainArr.join('')); // 求仟,佰,拾
    } else if (mainArr[mainArr.length - 1] === '角') {
      mainArr.pop(); // 去掉角
      currMoney = dic[mainArr[0]] * 0.1;
    } else if (mainArr[mainArr.length - 1] === '分') {
      mainArr.pop(); // 去掉分
      currMoney = dic[mainArr[0]] * 0.01;
    }
    totalMoney += currMoney;
  }

  return totalMoney.toString();
};

3.金额计算使用Decimal进行处理

你会发现在代码中用的是伍分,思路里是肆分。这就是因为js数值计算的误差,经典的题目:0.1+0.2  === 0.3 是true吗?答案是否。所以这里使用Decimal进行金额数值计算。下面给出的ts版本,其实和js一样,哈哈哈哈。

// 引入Decimal yarn add decimal.js 或者 npm install decimal.js --save
import { Decimal } from 'decimal.js';

// 大写汉字转数字;'叁仟肆佰伍拾亿零壹佰陆拾柒万叁仟肆佰伍拾壹' => '345001673451'
export const getMoneyNum = (money: string) => {
  // 大写汉字对应的数字
  const dic: any = {
    零: 0,
    壹: 1,
    贰: 2,
    叁: 3,
    肆: 4,
    伍: 5,
    陆: 6,
    柒: 7,
    捌: 8,
    玖: 9,
  };
  // 处理仟,佰,拾;'叁仟肆佰伍拾壹' => '3451'
  const getInfo = (item: string) => {
    let itemCount = new Decimal(0);

    // 将仟,佰,拾 拆分数组 '叁仟肆佰伍拾壹' => ['叁仟', '肆佰', '伍拾', '壹']
    let arr = item
      .replace(/(\w*仟)(\w*)/, "$1,$2")
      .replace(/(\w*佰)(\w*)/, "$1,$2")
      .replace(/(\w*拾)(\w*)/, "$1,$2")
      .split(',')
      .filter(_item => {
        return _item && _item.trim();
      });

    // 处理每个单位对应的值
    for (let subItem of arr) {
      let currValue = new Decimal(0);
      let subArr = subItem.split('');
      if (subArr[1] === '拾') {
        // 处理拾位
        currValue = new Decimal(dic[subArr[0]]).mul(new Decimal(10));
      } else if (subArr[1] === '佰') {
        //处理佰位
        currValue = new Decimal(dic[subArr[0]]).mul(new Decimal(100));
      } else if (subArr[1] === '仟') {
        // 处理仟位
        currValue = new Decimal(dic[subArr[0]]).mul(new Decimal(1000));
      } else {
        // 处理个位
        currValue = new Decimal(dic[subArr[0]]);
      }
      itemCount = new Decimal(itemCount).add(new Decimal(currValue));
    }
    return itemCount;
  };

  let totalMoney = new Decimal(0);

  /**
   * 按照亿,万,元,角,分拆分成数组;
   * '叁仟肆佰伍拾亿零壹佰陆拾柒万叁仟肆佰伍拾壹元捌角肆分' => ['叁仟肆佰伍拾亿', '壹佰陆拾柒万', '叁仟肆佰伍拾壹元', '捌角', '肆分']
   */
  let newMoney = money
    .replace(/零/g, '')
    .replace(/整/g, '') // 去掉 '零','整'
    .replace(/(\w*亿)(\w*)/, "$1,$2")
    .replace(/(\w*万)(\w*)/, "$1,$2")
    .replace(/(\w*元)(\w*)/, "$1,$2")
    .replace(/(\w*角)(\w*)/, "$1,$2")
    .split(',')
    .filter(_item => {
      return _item && _item.trim();
    });

  // 按照亿,万及以下单位为组, 循环求解求解数值;
  for (let mainItem of newMoney) {
    let currMoney = new Decimal(0);
    let mainArr = mainItem.split('');

    if (mainArr[mainArr.length - 1] === '亿') {
      mainArr.pop(); // 去掉亿单位
      currMoney = new Decimal(getInfo(mainArr.join(''))).mul(new Decimal(100000000)); // 求仟,佰,拾
    } else if (mainArr[mainArr.length - 1] === '万') {
      mainArr.pop(); // 去掉万单位
      currMoney = new Decimal(getInfo(mainArr.join(''))).mul(new Decimal(10000)); // 求仟,佰,拾
    } else if (mainArr[mainArr.length - 1] === '元') {
      mainArr.pop(); // 去掉元
      currMoney = new Decimal(getInfo(mainArr.join(''))); // 求仟,佰,拾
    } else if (mainArr[mainArr.length - 1] === '角') {
      mainArr.pop(); // 去掉角
      currMoney = new Decimal(dic[mainArr[0]]).mul(new Decimal(0.1));
    } else if (mainArr[mainArr.length - 1] === '分') {
      mainArr.pop(); // 去掉分
      currMoney = new Decimal(dic[mainArr[0]]).mul(new Decimal(0.01));
    }
    totalMoney = new Decimal(totalMoney).add(new Decimal(currMoney));
  }

  return totalMoney.toString();
};

4.去除包含其他汉字的大写金额字符串(如“人民币贰佰万元整”得到“贰佰万元”)

这里用的正则表达式进行处理的。我的业务场景是OCR识别营业执照返回的金额有“人民币”啥的字段信息。直接上代码。

// 去除字符串中其他文字,获取大写数字金额。如“人民币贰佰万元整”得到“贰佰万元”
const getRealMoney= (value) => {
  const regExp = /[零壹贰叁肆伍陆柒捌玖拾佰仟万亿元角分]/g;
  return value.match(regExp)?.join('');
};

5.保留金额到万位(输入值是stirng类型;231023310元 => 23102.331万元)

这里要考虑有角分的情况,和最后几位为零的情况。这里也主要是使用正则表达式。看代码。

// 保留金额到万
const getThousand = (money) => {
  let resReslut;
  const regExp = /\./g;
  // 有小数点 102321.10
  if (regExp.test('money')) {
    resReslut = money
      .replace(/(\d*)(\d{4}(?=\.))(\d*)/g, '$1.$2$3') // 102321.10 => 10.2321.10
      .replace(/(\d*)(\.)(\d*)(\.)(\d*)/, '$1$2$3$5') // 10.2321.10 => 10.232110
      .replace(/0*$/, '') // 去掉最后的'0' 10.232110 => 10.23211
      .replace(/\.$/, ''); // 去掉最后的'.'
  } else {
    // 231023310 => 23102.3310 => 23102.331
    resReslut = money
      .replace(/(\d)(\d{4}$)/, '$1.$2')
      .replace(/0*$/, '') // 去掉最后的'0'
      .replace(/\.$/, ''); // 去点最后的'.'
  }
  return resReslut;
};