1. 程式人生 > 其它 >正則表示式去掉小數點後面的0_前端使用正則表示式的常見場景(2)

正則表示式去掉小數點後面的0_前端使用正則表示式的常見場景(2)

技術標籤:正則表示式去掉小數點後面的0

文章內容整理自王峰老師的PPT。

常見場景一:正則與數值

  1. 數值的判斷

/[0-9]+/

[] 括起來的部分叫字符集;
使用連字元-表示指定字元的範圍(如果想匹配連字元可以進行轉義,或挨著方括號放置);0-9表示匹配從0-9的數字字元(常見的還有a-z匹配小寫字母,u4e00-u9fa5匹配漢字等);
如果只是匹配數字還可以使用d
+ 為限字元,匹配一個或多個

這個正則的缺點

不是全字元匹配,存在誤判

const reg = /[0-9]+/;
const str = 'a1';
reg.test(str); // true

/^d+$/

這個表示式,將字符集縮寫成了d,前後加了定位符

^ 匹配字串的開始位置。當結合m修飾符時,匹配某一行的開始位置
$ 匹配字串的結束位置。當結合m修飾符時,匹配某一行的結束位置
常見的定位符還有 :
b 匹配一個單詞的邊界,也就是字與空格間的位置
B 非單詞邊界匹配
const reg = /^d+$/;
const str = 'a1';
reg.test(str); // false

這個正則表示式的缺點

不能匹配帶符號的數值,如+1,-2

不能匹配小數,如3.1415926

/^[+-]?d+(.d+)?$/

(),圓括號內是一個子表示式,當圓括號不帶任何修飾符時,表示同時建立一個捕獲組
?,在正則表示式中有多種含義,作為限定符時,表示匹配0個或1個
.,.可以匹配除換行符之外的任意字元,結合修飾符s可以匹配包括換行符在內的任意字元

PS:想了解更多修飾符相關,可檢視正則表示式修飾符

const reg = /^[+-]?d+(.d+)?$/
reg.test("+1"); // true
reg.test("3.1415926"); // true

這個表示式的缺點

不能匹配無整數部分的小數,如.123

捕獲組會帶來額外的開銷

/^[+-]?(?:d*.)?d+$/

這個正則表示式中上個的區別是,上次是把小數點和小數部分放在括號裡的,這次把整數部分和小數點放在括號裡。

(?:),建立一個非捕獲組
*,限定符,匹配0個或多個
const reg = /^[+-]?(?:d*.)?d+$/
reg.test("+1"); // true
reg.test("3.1415926"); // true
reg.test(".314") // true

這個表示式的缺點

不能匹配無小數部分的數值,如2.

不能匹配科學計數法,如1e2,3e-1,-2.e+4

完整的數值正則

CSS Syntax Module Level 3​drafts.csswg.org

下面是一個完整的CSS數值token,在javascript需要多考慮一種情況,整數後邊帶小數點和科學計數法的表示在js中是合法的。

+'2.'; //2
+'2.e1'; // 20

7082f39bf2f3a43cb65e29b92b16bd74.png

2362dcd7219c8f77fa99973830be6488.png
分成正負符號兩個分支

表示分成正負符號兩個分支

9053350737ead16d651ed1b0f904bbc8.png

表示分成整數+小數點+整數;整數;小數點+整數三個分支

5573593a88898cf418fd34c5a8886684.png

表示分成有e部分和無e部分兩個分支,其中有e時又往下分為小寫e和大寫E兩個分支,分為正負符號兩個分支,整數。

/^[+-]?(?:d+.?|d*.d+)(?:e[+-]?d+)?$/i

|,用來建立分支,當位於圓括號內時,表示子表示式的分支條件,當位於圓括號外時,表示整個正則表示式的分支條件
i,修飾符表示匹配時忽略大小定

上面這個表示式還有沒有缺點呢?

實操

  1. 下面我們來實現一個函式,功能如下:
function execNumberList(){
...
}
console.log(execNumberList("1.0px .2px -3px +4e1px")); // [1,0.2,-3,40]
console.log(execNumberList("+1.0px -0.2px 3e-1px")); //[1,0.2,0.3]
console.log(execNumberList("1px 0px")); [1,0]
console.log(execNumberList("-1e+1px")); [-10]

函式如何實現才能輸入想要的結果呢?

const reg = /[+-]?(?:d*.)?d+(?:e[+-]?d+)?(?=px|s|$)/gi;
function execNumberList(str){
   reg.lastIndex = 0;
   let exec = reg.exec(str);
   const result = [];
   while(exec){
      result.push(parseFloat(exec[0]));
      exec = reg.exec(str);
   }
   return result;
}
console.log(execNumberList("1.0px .2px -3px +4e1px")); // [1,0.2,-3,40]
console.log(execNumberList("+1.0px -0.2px 3e-1px")); //[1,0.2,0.3]
console.log(execNumberList("1px 0px")); [1,0]
console.log(execNumberList("-1e+1px")); [-10]

/[+-]?(?:d*.)?d+(?:e[+-]?d+)?(?=px|s|$)/gi;

這個正則表示式跟之前的表示式有些類似,也有些區別

  1. 首先去掉了全字匹配
  2. 添加了g修飾符(在使用時重置了正則的lastIndex),表示全域性匹配,用於取出目標字串中所有符合條件的結果
  3. 增加了(?=px|s|$)

注意:

  • 按照CSS規範,只有數值為0才可以省略單位,這種情況沒有必要靠正則來過濾,可以先通過JS進行過濾處理
  • 這個例子只驗證了px單位,實際不存在pt,em,vw等單位,並且沒有考慮百分比的情況
  • 實際工作中,要根據需求再追加處理邏輯

斷言

(?=exp) 先行斷言(也稱零寬度正預測先行斷言,正向肯定環視或順序肯定環視)用於表示符合正則表示式的位置,匹配exp前面的位置。
var reg = /bw+(?=ingb)/gi;
reg.exec("I'm singing while you're dancing."); // ["sing", index: 4, input: "I'm singing while you're dancing.", groups: undefined]
reg.exec("I'm singing while you're dancing."); // ["danc", index: 25, input: "I'm singing while you're dancing.", groups: undefined]

先行斷言簡單的例子,正則表示式匹配以ing結尾的單詞的前面部分(除了ing以外的部分)

類似的斷言還有:

(?<=exp) 後行斷言(也稱零寬度正回顧後發斷言,反向肯定環視,逆序肯定環視)匹配exp後面的位置,es2018新增。
var reg = /(?<=bre)w+b/gi;
reg.exec("reading a reading a"); // ["ading", index: 2, input: "reading a reading a", groups: undefined]
reg.exec("reading a reading a"); // ["ading", index: 12, input: "reading a reading a", groups: undefined]
reg.exec("reading a reading a"); // null

正則表示式匹配以re開頭的單詞的後半部分(除了re以外的部分)

(?!exp)先行否定斷言(也稱零寬度負預測先行斷言,正向否定環視,順序否定環視)斷言此位置的後面不能匹配表示式exp
var reg = /d{3}(?!d)/;
reg.exec("123322"); // ["322", index: 3, input: "123322", groups: undefined]
reg.exec("1231234a123323"); // ["234", index: 4, input: "1231234a123323", groups: undefined]

正則表示式表示匹配三位數字,而且這三位數字的後面不能是數字

(?<!exp) 後行否定斷言(也稱零寬度負回顧後發斷言,反向否定環視,逆序否定環視),es2018新增,斷言此位置的前面不能匹配表示式exp
var reg = /(?<![a-z])d{7}/gi;
reg.exec("1234567 a1234567"); // ["1234567", index: 0, input: "1234567 a1234567", groups: undefined]
reg.exec("1234567 a1234567"); // null

正則表示式表示匹配前面不是小寫字母的七位數字

2. 數值轉貨幣格式

還是先來看一下最終要實現的目標

function formatCurrency(str){
 //...
}
console.log(formatCurrency('1'));// 1
console.log(formatCurrency('123'));// 123
console.log(formatCurrency('12345678'));//12,345,678

下面看一下如何通過正則輸出上面的結果

const reg = /(d)(?=(?:d{3})+(?:,|$))/g;
function formatCurrency(str){
  return str.replace(reg,'$1,');
}
console.log(formatCurrency('1'));// 1
console.log(formatCurrency('123'));// 123
console.log(formatCurrency('12345678'));//12,345,678
{n},限定符,表示重複n次,n必須是非負整數
類似的語法還有:
{n,m}表示重複n到m次,n和m都必須是非負整數,且n<=m
{n,} 表示重複n次以上
$n 用於replace的字串中,表示第n個捕獲組,n可以從1到9,一共可以匹配9個捕獲組
$& 表示本次完整的匹配,所以這段程式碼還可以寫成下面的方式:
const reg = /d(?=(?:d{3})+(?:,|$))/g; 
function formatCurrency(str){
  return str.replace(reg,'$&,');
}

PS:因為這裡只需要捕獲數字,後面是個先行斷言,所以可以改寫成上面的方式。

在es2018以上的環境,還可以使用後行斷言,只匹配個位置,所以只需要替換成逗號。

const reg = /(?<=d)(?=(?:d{3})+(?:,|$))/g;
function formatCurrency(str){
  return str.replace(reg,',');
}