爱客仕-前端团队博客园

记一次格式化金额风波

格式化金额之一般式

场景是这样,一件商品,会在页面上显示价钱,如果是街边上的一家煎饼馃子摊铺,那一般可能是这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<template>
<div>¥{{data.price}}元</div>
</template>
<script>
export default {
data() {
return {
data: {
price: 5
}
}
}
}
</script>

渲染结果如下:

1
<div>¥5元</div>

这样好像没什么问题,但是(一但有了但是,就好像有什么不好的事情发生),如果这是汤臣一品的一套房,那就是另一番景象了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<template>
<div>¥{{data.price}}元</div>
</template>
<script>
export default {
data() {
return {
data: {
price: 5000000000000000
}
}
}
}
</script>

渲染结果如下:

1
<div>¥5000000000000000元</div>

谁能告诉我,这是多少钱,我这辈子还有希望么,这还让不让人活了,感觉还是回家种田吧,大城市里太陌生,我干了这么多年的活,居然买不起一块地砖,呜。。。。。。。

好吧,这不是重点,谁让我们是屌丝呢,屌丝就是为土豪服务的,如果土豪来买房,连价钱都看不明白。那结果,土豪不开心,后果很严重,偶们的汤就快要没了,这个时候就是屌丝们唯一找到自信的地方了。
来吧,让俺们来为土豪抚平数不清零的忧伤,于是果断挽起裤腿,穿上衣服,开始干活,不要问我为什么还要穿上衣服,就是顺口而已,总不能说,脱掉衣服,开始干活吧(手动斜眼)。于是在俺们那余韵悠长,高潮迭起的键盘声中,俺们写下了如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<template>
<div>¥{{data.price|cashFormat}}元</div>
</template>
<script>
export default {
filters: {
cashFormat(cashNumber) {
let arr = []
(cashNumber + '').split('').reverse().forEach((item, i) => {
arr.push(i > 0 && i % 3 === 0 ? item + ',' : item)
})
return arr.reverse().join('')
}
},
data() {
return {
data: {
price: 5000000000000000
}
}
}
}
</script>

渲染结果如下:

1
<div>¥5,000,000,000,000,000元</div>

终于撸完,屌丝深邃的眼神中,露出了久违的满足感,那种睥睨天下的感觉————看哥的代码,多么棒的链式调用,多么少的代码,多么工整的格式。。。。。。

然而,灰太狼总会遇到红太狼,熊大熊二总能碰上光头强,天空突然飘来五个字,这TM一行代码能搞定的事,你给老子写这么长,写这么长,这么长,么长,长。。。

自从撸完了这一段代码,屌丝从未受过如此挑衅,虽然对这五个字不屑一顾,但内心时不时总会想起,这个能一行搞定?这个能一行搞定?屌丝不停的问自己,不行,就算能一行搞定,那也一定要俺自己来实现,屌丝满世界的寻找答案(。。。此外省略n多情节),直到有一天,屌丝终于找到了答案。

格式化金额之必杀式

没错,这就是传说的打通任督二脉的无上心法《正则表达式》,出于对屌丝的无限崇拜,俺怀着忐忑的心情,在一个偶然的机会,向屌丝请教,屌丝看我还算长得,玉树临风,英俊潇洒,一表人才,才疏学浅,浅薄无知。。。,才终于传了我一招半式:

  • \d大家都知道,是代表数字
  • \d{3}大家也都知道是代表3个数字
  • ()这个大家知道是把一个东西括起来当成一组
  • (\d{3})那这样就是把三个数字当成一组
  • +大家知道是重复
  • (\d{3})+那这样就是重复三个一组
  • $这个大家知道是行尾
  • (\d{3})+$那这个就是三个一组的数字到行尾

听到这里,俺若有所悟,这个简单,俺知道答案了,于是俺写下了如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<template>
<div>¥{{data.price|cashFormat}}元</div>
</template>
<script>
export default {
filters: {
cashFormat(cashNumber) {
return cashNumber.replace(/(\d{3})+$/g, ',$1')
}
},
data() {
return {
data: {
price: 5000000000000000
}
}
}
}
</script>

渲染结果如下:

1
<div>¥5,000元</div>

神马,后面的0呢?俺百撕不得骑jie,终于知道,俺的道行还不够,于是又恬着脸,去向屌丝请教,屌丝展现出了超越常人的风度,是曰:知错能改,善莫大焉!并接着说道:孩子,下面才是真正的秘籍,一般人我是不传滴,我是看你长得玉树临风,英俊潇洒,一表人才,才疏学浅,浅薄无知。。。这才教你,并大喝一声,看着:

  • (?!____)这个叫排除
  • (?=____)这个叫必须

我不解,只听屌丝解释道:排除就是后面必须不是,必须就是后面必须是,并且这两个都是只向后看一看,至于到底要在字符串中间找什么,还得看其它,看下面代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<template>
<div>¥{{data.price|cashFormat}}元</div>
</template>
<script>
export default {
filters: {
cashFormat(cashNumber) {
return (cashNumber + '').replace(/(?!^)(?=(?:\d{3})+$)/g,',')
}
},
data() {
return {
data: {
price: 5000000000000000
}
}
}
}
</script>

渲染结果如下:

1
<div>¥5,000,000,000,000,000元</div>

看到输出的结果,我发出由衷的赞叹,但,这么长一串,看不懂呀,只得求教,屌丝解释道:
/(?!^)(?=(?:\d{3})+$)/g,这句代码的意思就是,排除行首,并且后面的数字刚好是3的倍数的位置的地方,换成一个,号,后面g的意思是,找到一个这个位置还不行,再看看,还有没有这样的位置,有的话,接着换。
我听完恍然大悟,拜倒在地。。。。

后记

本来以为就这么一个小例子,应该很容易讲清楚,但真写的时候,发现中间有的细节,如*+?这些个东西的组合,用打字实在是能当成写小说了,太多,说不完,我想应该是我语言组织能力不好。另外这两个,网上一般都叫正向预查:

  • (?!____)这个叫排除
  • (?=____)这个叫必须

其实还有反向预查,但js不支持,我就不展开了,可以说,要熟练运用正则表达式,这两个是一定少不了的,比如说,匹配一个数字,可以有+-号前缀,可以有两位小数,我不知道大家会怎么写,我可能会这样:

1
const testNumberReg = /(?!^[-+]?00)^[-+]?((0(\.\d{1,2})?$)|[1-9]\d*(\.\d{1,2})?$)/

写了这么多,算是抛砖引玉,文中不免有谬误,还希望大家指教。