利用正则实现千分位分隔| 8月更文挑战

·  阅读 951
利用正则实现千分位分隔| 8月更文挑战

前言

在一些涉及到比较大的数字的场景中,比如人数,金额等,我们常常需要对这些数字进行千分位,然后逗号分隔,这样会比较好阅读。

比如

123456789
复制代码

如果是千分位分隔

123,456,789
复制代码

对比这两种,我们肯定是选择第二种,方便阅读。

实现

那怎么实现呢?

一般是利用正则表达式

function formatNumber(num) {
  const reg = /(\d)(?=(\d{3})+$)/g
  return num.toString().replace(reg, '$1,')
}
formatNumber(12345678) // "12,345,678"
formatNumber(8765432188) // "8,765,432,188"
复制代码

测试通过。

但是这是基于数字是整数型的正则,如果你的数字是有小数点的,结果如下

formatNumber(1234.5678) // "1234.5,678"
复制代码

期望是"1,234.5678",不需要对小数点部分做千分位分隔,只需要对整数部分做千分位分隔,

实际是 "1234.5,678"

所以需要对正则进行优化。

function formatNumber(num) {
  const reg = /(\d)(?=(\d{3})+\.)/g
  return num.toString().replace(reg, '$1,')
}
formatNumber(1234.5678) // "1,234.5678"
formatNumber(1234) // "1234"
复制代码

从上面例子可以看到,

如果数字带有小数点,则是有效果的

但是如果数字是没有小数点的,这个正则是不起作用的, 所以需要对两个正则整合起来,实现对整数和小数的支持

function formatNumber(num) {
  num = num.toString()
  let reg = num.indexOf('.') > -1 ? /(\d)(?=(\d{3})+\.)/g : /(\d)(?=(\d{3})+$)/g
  return num.replace(reg, '$1,')
}
formatNumber(1234.5678) // "1,234.5678"
formatNumber(1234) // "1,234"
复制代码

下面对正则/(\d)(?=(\d{3})+$)/g/(\d)(?=(\d{3})+\.)/g进行分析:

  1. 这个正则主要是利用d{3}每3位数字匹配一次,匹配到就对前面的那个数字加上逗号,以此类推。
  2. 正则这里加了$.,是为了优先后面的凑成3位,开头如果不足3位就不处理。
  3. 这里还使用了正则一个表达式(?=pattern),术语叫零宽断言,或者也叫正向肯定预查

实现千分位分隔,核心点就是这个,下面来讲讲这个正则表达式。

零宽断言(正向肯定预查)

(?=pattern), 是指配合正则表达式进行匹配,但是不获取该匹配。下次匹配还是从该位置开始。

配合例子来讲会比较好理解:

var regA = /答案(?=cp3)/
console.log('答案cp3'.match(regA)) // ["答案"]

var regB = /cp3(?=cp3)/g
console.log('cp3cp3cp3cp3'.match(regB)) // ["cp3", "cp3", "cp3"]
复制代码

第一个例子可以看到匹配到了答案cp3,但是只获取到了答案cp3不获取

第二个例子可以看到匹配到了cp3cp3,只获取到了cp3,下次匹配从第二个cp3开始,以此类推,最终输出三个cp3

结合上面的千分位分隔例子,因为我们使用了零宽断言,因为它下次匹配还是从该位置开始,所以就导致每个数字都会被匹配到,直到结尾

let reg = /(\d)(?=(\d{3})+)/g
'1234567890'.replace(reg, (match,$1, $2, index, str) => {
 console.log(match,$1, $2, index, str)
 return $1 + ','
})

输出 1 1 890 0 1234567890
输出 2 2 678 1 1234567890
输出 3 3 789 2 1234567890
输出 4 4 890 3 1234567890
输出 5 5 678 4 1234567890
输出 6 6 789 5 1234567890
输出 7 7 890 6 1234567890
返回 "1,2,3,4,5,6,7,890"
复制代码

所以我们在零宽断言里面加上$或者.去约束条件,限制优先匹配结尾,开头不足3位就不处理

let reg = /(\d)(?=(\d{3})+$)/g
'1234567890'.replace(reg, (match,$1, $2, index, str) => {
 console.log(match,$1, $2, index, str)
 return $1 + ','
})
输出 1 1 890 0 1234567890
输出 4 4 890 3 1234567890
输出 7 7 890 6 1234567890
返回 "1,234,567,890"
复制代码

这样子就没有问题了。

总结

以上就是利用正则实现千分位分隔的总结

完整代码:

function formatNumber(num) {
  num = num.toString()
  let reg = num.indexOf('.') > -1 ? /(\d)(?=(\d{3})+\.)/g : /(\d)(?=(\d{3})+$)/g
  return num.replace(reg, '$1,')
}
复制代码
收藏成功!
已添加到「」, 点击更改