在学千分位分割的过程中,有一种方案是使用正则表达式。
function formatNumberWithCommas(number) {
const fixedNum = number.toFixed(20);
let digitCount = 20;
for (let i = fixedNum.length - 1; i >= 0; i--) {
if (fixedNum[i] === '0') {
digitCount--;
} else {
break;
}
}
const parts = number.toFixed(digitCount).split('.');
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ',');
return parts.join('.');
}
const number = 126536536536.276376273265;
const formattedNumber = formatNumberWithCommas(number);
console.log(formattedNumber); // "126,536,536,536.276376273265"
核心逻辑是中间的正则表达式,chatgpt 给我解释了:
\B:表示非单词边界,匹配不在单词边界的位置。这确保我们在插入逗号时不会影响单词的边界。(?=(\d{3})+(?!\d)):这是一个正向断言,表示匹配在当前位置后面的内容,但不会消耗字符串中的字符。它的含义是在当前位置后面查找至少一组连续的三个数字,且这些数字后面不能跟着另一个数字。这保证了我们只会在数字之间插入逗号。(?!\d):这是一个负向断言,表示在当前位置后面查找不跟着数字的内容。这是为了确保我们在插入逗号时不会破坏原有的数字格式。
综合起来,这个正则表达式会在每三个连续的数字之间插入逗号,从而实现千分位分隔。
这里出现了两个词:正向断言和负向断言。它们存在的意义是匹配位置,不会消耗字符。
零宽断言是个啥
零宽断言(Zero-Width Assertions)是正则表达式中一种高级的匹配技术,用于指定一个位置的前面或后面应该满足的条件,而不会实际匹配或消耗字符。换句话说,它们允许你在一个位置上进行条件匹配,但不会在字符串中移动位置。
零宽断言并不会真正匹配字符,而只是查看位置是否满足特定的条件。它们在处理复杂的匹配需求时非常有用,例如在特定位置插入内容、进行条件判断等。
在正则表达式中,有以下四种主要类型的零宽断言:
- 正向肯定断言(Positive Lookahead Assertion):
(?=...)正向肯定断言用于查看在当前位置后面是否满足给定条件。 - 正向否定断言(Negative Lookahead Assertion):
(?!...)正向否定断言用于查看在当前位置后面是否不满足给定条件。 - 反向肯定断言(Positive Lookbehind Assertion):
(?<=...)反向肯定断言用于查看在当前位置前面是否满足给定条件。 - 反向否定断言(Negative Lookbehind Assertion):
(?<!...)反向否定断言用于查看在当前位置前面是否不满足给定条件。
这些断言在匹配的过程中不会改变字符串的位置,它们只是在特定位置上进行条件判断。通过使用零宽断言,你可以在不匹配实际字符的情况下,更加精确地指定匹配的位置。
replace 为什么是插入
在正则表达式中,当我们使用 \B(?=...) 来查找位置时,我们实际上是在匹配一个零宽断言(zero-width assertion),而不是匹配实际的字符。这意味着,我们在找到位置后,并不会直接替换这个位置上的字符,而是在这个位置前面插入逗号。
在正则表达式的 replace 方法中,首先会匹配所有满足条件的位置,然后将这些位置替换为指定的内容。换句话说,replace 方法会首先找到所有要替换的位置,然后在一次性替换这些位置上的内容。
仔细理解零宽断言
哎,看了上面chatgpt的解释,我对断言的运作还是云里雾里的,尤其是不理解为什么要有(!=\d),只能上regexr试一试了。
下方显示了插入的位置,好嘞,我们来分析一下为什么是这些位置。
1234567 890:只看890,满足三位数字出现一次(正向断言);890的后面啥也没有,满足不是数字(负向断言)。所以这个位置是满足的。
1234 567890:只看567890,满足三位数字出现两次;同理,后面啥也没有,满足不是数字。所以这个位置是满足的。
后面的就不分析了。
假如把(!=\d)去掉:
123456 7890:从7出发,找到有789三个数字,这个位置就满足了,因为此时并没有要求后面不能是数字。
12345 67890:从6出发,找到有678三个数字,这个位置就满足了。
其余同理,所以(!=\d)是必须的。“不是数字的字符”被限制到整个数的尾部,再结合?=,就可以找到距离末尾距离为3、6、9...的位置,也就是千分位分隔点。