1. 程式人生 > >千位分隔符的完整攻略

千位分隔符的完整攻略

edi spa ctu 千分位 urn 允許 重新 一起 技術

千位分隔符[1]是很常見的需求,但是輸入文本千變萬化,如何才能準確添加千分符呢?

純整數情況

技術分享圖片

純整數大概是所有情況裏最簡單的一種,我們只要正確匹配出千分位就好了。

觀察上面的數字,我們可以得出千分位的特征是到字符串終止位有 3n 個數字,不包括起始位。於是可以得到這樣的函數:

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

但是往往現實沒有那麽樂觀:

小數的情況

技術分享圖片

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

如果正則引擎支持逆序環視[2],我們可以這樣構造正則表達式:

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

但是多數語言並不支持逆序環視,所以我們要變通一下:

1. 拿到小數的整數部分

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

^\d+

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

這一步可以利用我們之前的實現,整合在一起如下:

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

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

技術分享圖片

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

整、小數混合字符串

技術分享圖片

這時我們就不能繼續用字符串起終點 ^$ 來判定邊界了,如果改成單詞邊界 \b 會發生什麽呢:

技術分享圖片

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

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

\b\d+

\b 的界定是 (?<!\w)(?=\w)|(?<=\w)(?!\w)[3],所以小數點也被視為單詞邊界了!所以我們不應該用單詞邊界作為界定條件,重新看剛才的字符串 ‘12345678 1234.5678‘,可以發現整數部分的起始點都有一個特征:要麽位於字符串起點,要麽跟在空白符後。基於這點我們修改捕獲整數部分的正則如下:

(^|\s)\d+

技術分享圖片

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

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

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

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

完整函數如下:

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

技術分享圖片

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

復雜字符串

技術分享圖片

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

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

依據這個我們可以這樣做:

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

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

技術分享圖片

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

let 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, ‘,‘))
})()

技術分享圖片

酷炫!全部都正確處理了!

復雜的現實世界

但是!這還遠遠不夠!我們看這樣一個字符串:

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

容我先去買一根上吊繩……

千位分隔符的完整攻略