以前说到正则表达式,我以为就是匹配一些字符,然后拿到匹配的元素一顿操作,或者使用类似String.replace
这种方法对匹配到的元素进行替换。但是最近发现,它也能匹配空字符,并做一些事情。
考虑这个问题:我要把123456789
这个数字从个位数起每隔三位加一个,
——即把它变成123,456,789
这种格式,要怎么做?这是一个很实用的问题,与钱相关的东西或多或少都会被要求处理格式。
我们可以用Javascrpt来处理,实现它的方式有很多,比如这样:
function formatCash (num) {
num += '';
if (/^(-?)(\d+)(\.\d+)?$/.test(num)) {
let absFloorCash = RegExp.$2;
let digit;
let tcash = absFloorCash.split('').map((item,index) => {
if ((digit = absFloorCash.length - index - 1)
&& index !== 0
&& (digit % 3 === 0)) {
return item + ',';
} else {
return item;
}
}).join('');
return RegExp.$1 + tcash + RegExp.$3;
} else {
throw "输入的现金格式不对:" + num;
}
}
console.log(formatCash(-32123998556.12562)); // -32,123,998,556.12562
console.log(formatCash(236522623)); // 236,522,623
console.log(formatCash(0.236562)); // 0.236562
console.log(formatCash(15262622.36522)); // 15,262,622.36522
其实Number
对象有一个叫做toLocaleString
的方法,也可以直接帮我们搞定:
(123890999.123).toLocaleString(); // "123,890,999.123"
那用正则怎么写呢?
function formatCashByReg (num) {
num += '';
return num.replace(/(?<=^-?\d+)(?=(\d{3})+(\.|$))/g, ',');
}
console.log(formatCashByReg(-32123998556.12562)); // -32,123,998,556.12562
console.log(formatCashByReg(236522623)); // 236,522,623
console.log(formatCashByReg(0.236562)); // 0.236562
console.log(formatCashByReg(15262622.36522)); // 15,262,622.36522
实现起来非常简洁,用到的是ES6提供的正则环视(lookahead / lookbehind)功能:
规则 | 功能 |
---|---|
x(?=y) | x后面紧跟y时才匹配 |
x(?!y) | x后面没紧跟y才匹配 |
(?<=y)x | x前面紧跟y时才匹配 |
(?<!y)x | x前面没紧跟y时才匹配 |
来看看我们的正则,其实它是由两个环视构成的,一个是后环视(?<=^-?\d+)
,另外一个是前环视(?=(\d{3})+(\.|$))
。先来看第一个,去掉环视后就是^-?\d+
,表明从字符串开头到我们的目标位置,可以在开头有一个或零个-
,然后紧跟的是一串数字(注意不能有小数点.
)。再来看第二个,去掉环视后是(\d{3})+(\.|$)
,表明从目标位置开始,后面依次紧跟的是n个三位数的组合,然后紧跟小数点或者字符串末尾。
这个正则的含义足够直接,但是注意它没有匹配到任何字符,而只是匹配了一个“位置”,这个“位置”的后方满足后环视的正则,前方满足前环视的正则,在replace
操作时只是将这个位置匹配到的空字符""
替换为了","
。
初次见到这个我是比较懵逼的,竟然还能匹配空字符?正则还真是一个神奇的东西,用得灵活的话可以简化很多操作。这只是个简单的例子,但是提供了一种解决类似问题的思路。