Skip to the content.

如何用四行代码实现数字的千分位分割?

前言

最近遇到一道手写面试题,感觉还比较有趣,分享给大家。

要求实现一个千分位逗号分割的方法。 小数位也需要分割。

例如:12345678.1415926 ==》 '12,345,678.141,592,6'

最终实现版本

借助正则,精简到四行代码。

//千位分隔符
function formatNumber(num) {
  var source = String(num).split("."); //按小数点分成2部分
  source[0] = source[0].replace(new RegExp("(\\d)(?=(\\d{3})+$)", "ig"), "$1,"); //只将整数部分进行都好分割
  source[1] = source[1].replace(new RegExp("(\\d{3})", "ig"), "$1,");
  return source.join("."); //再将小数部分合并进来
}
formatNumber(12345678.1415926);

相关正则的讲解

\d表示数字。 $ 符号代表结尾。

?= 是非捕获元, 表示匹配前面的。 例如: exp1(?=exp2):查找 exp2 前面的 exp1。

{3}表示 3 次,\d{3} 表示 3 个相连的数字。

i: 表示不区分大小写,这里也可以不用。

g: 表示全部匹配。

$1: 表示引用第一个元组。小括号包裹的算作一个组。

+: 表示匹配前面的表达式一次或多次。

为什么这里使用双反斜杠?

首先字符串中的\被编译器解释为\         ——-》   第一步,编译器将字符串转变为“正则表达式”\”, 然后作为正则表达式,\.又被正则表达式引擎解释为.   —————-> 第二步,才开始把第一步的结果当做是正则表达式,开始进行匹配!

如果在字符串里只写\.的话,第一步就被直接解释为.,之后作为正则表达式被解释时就变成匹配任意字符了

先解释一下(\\d)(?=(\\d{3})+$)

首先看?=后面是是 3 个数字为一组,匹配一次或多次,就是从后面数,找 3 的倍数个数字。 其实?=表示匹配前面的,也就是匹配到的是 ‘12345’, ‘12’, 分别替换为$1,,效果就是在其后添加逗号。

小数部分

有了上面的基础,小数后面的匹配就更简单了,从前面数,每三个数字后面添加逗号就好了。

直接写成(\\d{3})即可。

其他实现方式?

这里我想说一下我第一版实现的方式

主要思路是:

  1. 小数点分割成整数部分和小数部分;
  2. 整数部分倒置之后,可和小数部分一样处理;
  3. 从前往后,三位分割成一个数组
  4. 分组之后用逗号 join
  5. 整数部分倒置恢复
  6. 拼接整数部分与小数部分
  7. 完成
//千位分隔符
function formatNumber(num) {
  var source = String(num).split("."); //按小数点分成2部分
  return reverseStr(sepNum(reverseStr(source[0]))) + "." + sepNum(source[1]);
}
// 字符串翻转
function reverseStr(str) {
  return str.split("").reverse().join("");
}
function sepNum(num) {
  // 这里之所以切成数组是因为方便后面执行Splice方法
  !Array.isArray(num) && (num = num.split(""));
  // 准备一个数组用于盛放切割之后的数组
  const res = [];
  while (num.length > 3) {
    // 每三位切割
    res.push(num.splice(0, 3).join(""));
  }
  // 最后不大于三位的数字直接推到数组中
  res.push(num.join(""));
  // 字符串组装
  return res.join(",");
}
formatNumber(12345678.1415926);

image.png

执行结果虽然没啥问题,但是两次反转字符显得不够聪明。

也许选择其他的字符串翻转方法,性能会不会好一点。

// 字符串翻转二
function reverseStr(str) {
  let res = "";
  for (let i = str.length; i; i--) {
    res += str[i - 1];
  }
  return res;
}

返回首页