2014/10/02

[Regex]加入數字的千分位(thousands separator)

因為Javascript的Regex沒有lookbehind, 所以一般在處理為數字加上千分位的作法會受到一些限制.
以下先說明數字加上千分位的規則:
  1. 數字的整數部分每3位數插入一個逗號(,), ex: 123456 –> 123,456
  2. 小數點後不加入千分位, ex: 123456.1234 –> 123,456.1234
從字串的比對方式來說, 就是當1-3個數字字元, 其右方的數字字元長度是3的倍數時, 就為該比對到的Pattern最後補上一個逗號, 並依此類推至整個字串結束.
所以一開始會有人寫成以下這樣的Regex Pattern:
"1234567".replace(/(\d{1,3})(?=(\d{3})+)/g, "$1,");
結果發現, 怎麼不是1,234,567而是123,4,567 !?
原因在於以下的比對步驟:
  1. 因為是比對 (\d{1,3})(?=(\d{3})+), 所以第一個比對到的會是1234, 12345, 123456.
  2. 由於是用(\d{1,3}), 取最長的3字元, 所以是123456被轉換成123,.
  3. 接下來, 比對到4567, 所以被轉換成4,.
  4. 最後結果就變成123,4,567
所以, 問題出在後面lookahead的條件, 應該是要以3個字元為單位, 直到字串結束為止.
"1234567".replace(/(\d{1,3})(?=(\d{3})+$)/g, "$1,");
整數大概這樣就可以結束了, 但這樣的規則套用到浮點數的結果會是如何?
"1234567.654321".replace(/(\d{1,3})(?=(\d{3})+$)/g, "$1,");
結果會是: 1234567.654,321
多了一個小數點, 整數部分便完全不是我們要的格式, 且小數部分反而被加上千分位了.
再回頭看lookahead的部分, 我們是希望小數點前的數字可以被比對成功, 小數後不可被替換.
所以改成以下的方式:
"1234567.654321".replace(/(\d{1,3})(?=(\d{3})+\.)/g, "$1,");
結果就會是我們要的1,234,567.654321
但這個Regex Pattern不能套用在整數上, 因為整數無小數點.
結論:
//整數用
"1234567".replace(/(\d{1,3})(?=(\d{3})+$)/g, "$1,");
//浮點數用
"1234567.1234".replace(/(\d{1,3})(?=(\d{3})+\.)/g, "$1,");

沒有留言:

張貼留言