千位分隔符的完整攻略

千位分隔符[1]是很常見的需求,可是輸入文本變幻無窮,如何才能準確添加千分符呢?javascript

純整數狀況

![](/content/images/2015/06/integer.png)

純整數大概是全部狀況裏最簡單的一種,咱們只要正確匹配出千分位就行了。java

觀察上面的數字,咱們能夠得出千分位的特徵是到字符串終止位有 3n 個數字,不包括起始位。因而能夠獲得這樣的函數:git

javascriptlet milliFormat = (num) => {
    return num && num.toString().replace(/(?=(?!^)(\d{3})+$)/g, ',')
}

可是每每現實沒有那麼樂觀:正則表達式

小數的狀況

float

遇到小數時,咱們的但願只針對整數部分添加千分符,這時問題就變得稍稍有些棘手了。segmentfault

若是正則引擎支持逆序環視[2],咱們能夠這樣構造正則表達式:函數

javascript(?<=^\d+)(?=(\d{3})+\b)

可是多數語言並不支持逆序環視,因此咱們要變通一下:spa

1. 拿到小數的整數部分

也就是起始位到小數點(非數字)之間的部分,能夠這樣實現:.net

javascript^\d+

2. 爲整數部分添加千分符

這一步能夠利用咱們以前的實現,整合在一塊兒以下:3d

javascriptlet milliFormat = (num) => {
    return num && num.toString()
        .replace(/^\d+/g, (m) => m.replace(/(?=(?!^)(\d{3})+$)/g, ','))
}

這個函數對整、小數都能正確處理:code

result-1

但在實際中,咱們還可能傳入一個整、小數混合的字符串:

整、小數混合字符串

mixture

這時咱們就不能繼續用字符串起終點 ^$ 來斷定邊界了,若是改爲單詞邊界 \b 會發生什麼呢:

result-2

哦不!連小數部分也被添上千分符了!怎樣才能避開小數部分?

從新審視咱們捕獲整數部分所用到的正則:

javascript\b\d+

\b 的界定是 (?<!\w)(?=\w)|(?<=\w)(?!\w)[3],因此小數點也被視爲單詞邊界了!因此咱們不該該用單詞邊界做爲界定條件,從新看剛纔的字符串 12345678 1234.5678,能夠發現整數部分的起始點都有一個特徵:要麼位於字符串起點,要麼跟在空白符後。基於這點咱們修改捕獲整數部分的正則以下:

javascript(^|\s)\d+

result-3

咦,多出來一個空白符?彆着急,看看咱們用來匹配千分位的正則:

javascript(?=(?!^)(\d{3})+$)

判斷條件是非起點、到結尾有 3n 個數字的位置,如今爲了去掉這多出來的一個空格,咱們應將起始條件改爲單詞邊界:

javascript(?=(?!\b)(\d{3})+$)

完整函數以下:

javascriptlet milliFormat = (input) => {
    return input && input.toString()
        .replace(/(^|\s)\d+/g, (m) => m.replace(/(?=(?!\b)(\d{3})+$)/g, ','))
}

result-4

酷炫!咱們已經能自如應付各類數值的混合了!這是耳邊幽幽飄來產品經理的聲音:若是我傳入含有非數字的字符串呢……

複雜字符串

complex

在上一個例子中,咱們只判斷了起始邊界,因而 1234ww 中的數字部分也會被捕獲。爲了解決這個問題,咱們要加上終止界定。來看看整、小數成立的條件:

字符串中僅包含有數字 0-9 或小數點

依據這個咱們能夠這樣作:

javascript(^|\s)\d+(?=\.?\d*($|\s))

這個正則表示匹配目標應以字符串起始位或空白符開始,緊接着是數字,數字的右邊只容許繼續是數字或者一個小數點、直到字符串結尾或下一個空格處。來看看它的匹配效果:

result-5)

好樣的!咱們已經能精確匹配出正確的部分了!繼續用以前的千分位模式封裝:

javascriptlet milliFormat = (() => {
    const DIGIT_PATTERN = /(^|\s)\d+(?=\.?\d*($|\s))/g
    const MILLI_PATTERN = /(?=(?!\b)(\d{3})+$)/g

    return (input) => input && input.toString()
        .replace(DIGIT_PATTERN, (m) => m.replace(MILLI_PATTERN, ','))
})()

result-6

酷炫!所有都正確處理了!回家睡覺!

so-dick

更復雜的世界

@vc1 指出,在現實中可能還會有更加複雜的狀況,如:

'1234 1234.56 $1234 $-1234 $-1234.56e+7 123...e3'

容我先去買根上吊繩……

(全文完)

參考資料

  1. 小數點 - 維基百科
  2. 正則基礎之——環視 - 雁過無痕
  3. 正則基礎之——\b 單詞邊界 - 雁過無痕

重編自個人博客,原文地址:https://idiotwu.me/milli-formatting-digitals-with-regex/

相關文章
相關標籤/搜索