1. 程式人生 > 實用技巧 >JS 正則 長篇

JS 正則 長篇

正則

正則:
一個規則, 用來處理字串的一個規則

處理:

處理

reg.test(str)

判斷一個字串是否複合我們的制定的規則

var reg = /\d/;
console.log(reg.test("lemon22")); // true
console.log(reg.test("lemon"));   // false

捕獲

reg.excec(str)

把字串中符合正則規則的內容捕獲到

var reg = /\d/;
console.log(reg.exec("lemon")); // null
console.log(reg.exec("1")); // ["1", index: 0, input: "1"];

如何建立一個正則

字面量方式:

var reg = /\d/;

例項建立方式

var reg = new RegExp("");

兩種方式建立區別

需要動態建立正則表示式:

  • 在字面量方式中, 我們//之間包起來的所有的內容都是元字元, 有的具有特殊的意義, 大部分代表本身含義的普通的元字元
var name = "lemon"
var reg = /^\d+"+name+"\d+$/g
console.log(reg.test('22"""lemon""22')) // true
  • 對於這樣的需求, 我們只能使用例項建立的方式了
var name = "lemon"
var reg = new RegExp("^\\d+" + name +"\\d+%", "g")
console.log(reg.text("22lemon""")) // true

字面量和例項建立的方式在正則中的區別?

  • 字面量方式中出現的一切都是元字元, 所以不能進行變數值的拼接, 而例項建立的方式是可以的
  • 字面量方式中直接寫\d就行了, 在例項建立方式中需要把它轉移 \d

元字元

每一個正則表示式都是由元字元和修飾符組成的,

元字元:
在//之間具有意義的一些字元

1.常用特殊意義的字元

  • \: 轉移字元, 轉移後面字元所代表的含義
  • \n: 匹配一個換行符
  • . : 除了\n以外的任意字元
  • ( ): 分組, 把一個大正則本身劃分為幾個小的正則
  • x | y: x或者y中的一個字元
  • [xyz ]: x或者y或者z中的一個字元
  • [^xyz]: 除了三個以外的任何一個字元
  • [a-z]: a-z之間的任何一個字元
  • [^a-z]: 除了a-z之間的任何一個字元
  • \d: 一個0-9之間的數字
  • \D: 除了0-9之間的數字以外的任何字元
  • \b: 一個邊界符 "a b" -> "\ba\b\bb\b"
  • \w: 數字, 字母, 下劃線中的任意字元 等價於 [0-9a-zA-z_]
  • \s: 匹配一個空白字元 空格, 一個製表符, 換頁符....

2.代表出現次數的量詞元字元

  • *: 出現0到多次
  • +: 出現一次到多次
  • ?: 出現零或一次
  • {n} : 出現n次
  • {n, }: 出現n到多次
  • {n, m}: 出現n到m次

3. 定位符,

  • ^: 以某一個元字元開始
  • $: 以某一個元字元結束
  • \b: 匹配位於每個單詞的開始或結束的位置
  • \B: 匹配每個每個單詞的不是開始或結束的位置,即單詞中間的位置
  • \A: 匹配字串的開始位置
  • \Z: 匹配字串的結束位置

注意:

[ ]: 中出現的所有的字元都代表本身意思的字元(沒有特殊的含義)

分組

改變優先順序

var reg = /^18|19$/

18, 19, 181, 189, 119, 819, 1819...

var reg = /^(18|19)$/

18, 19

中括號不識別兩位數

var reg = /^[12-68]$/

1, 2-6中的一個, 8

示例

匹配有效數字
var reg = /^[+-]?(\d|([1-9]\d+))(\.\d+)?$/

[+-]?
可以有+或-, 或者都沒有

(\d|([1-9]\d+))
單個數字(包括0), 或者以1到9開頭的多位數字

(.\d+)?
以.開頭的多位數字, 或者都沒有

年齡介於18-65之間
var reg = /^(1[8-9]|[2-5]\d|6[0-5])$/
郵箱(簡版)

[email protected]
[email protected]
[email protected]
[email protected]

左邊: 數字, 字母, 下劃線, ., -
@
右邊:

var reg = /^[\w.-]+@[0-9a-zA-z]+(\.[a-zA-Z]{2,4}){1,2}$/

標準真實姓名 2-4位漢字

var reg = /^[\u4e00-\u9fa5]{2, 4}$/

身份證號碼(簡版)

var reg = /^\d{17}(\d|X)$/

捕獲

捕獲的內容格式:
每一次捕獲的時候都是先進行預設的匹配, 如果沒有匹配成功的, 捕獲的結果是null; 只有有匹配的內容才能捕獲到:

  • 捕獲到的內容是一個數組
    • 陣列中的第一項是當前大正則捕獲到的內容
    • index: 捕獲內容在字串中開始的索引位置
    • input: 捕獲的原始字串

懶惰性, 貪婪性

正則捕獲的特點

  • 懶惰性: 每一次執行exec只捕獲第一個匹配的內容, 在不進行處理的情況下, 在執行多次捕獲, 捕獲的還是第一個匹配的內容
    • lastIndex: 是正則每一次捕獲在字串中開始查詢的位置
  • 貪婪性: 正則的每一次捕獲都是按照匹配最長的結果捕獲的, 例如: 2符合正則, 2020頁符合正則, 我們預設捕獲的是2020
var reg = /\d+/;
var str = "lemon2020age22";
var res = reg.exec(str);
console.log(res);

解決懶惰性

在正則的末尾加一個修飾符"g"

修飾符:g, i, m

  • global(g): 全域性匹配
  • ignoreCase(i): 忽略大小寫匹配
  • multiline(m): 多行匹配

加了全域性修飾符g, 正則每一次捕獲結束後, 我們的lastIndex的值都變為了最新的值, 下一次捕獲從最新的位置開始查詢, 這樣就可以把所有需要捕獲的內容都捕獲到了

var reg = /\d+/g;
var str = "lemon2020age22";
console.log(reg.lastIndex);
console.log(reg.exec(str));
console.log(reg.lastIndex);
console.log(reg.exec(str));

0
Array["2020", ...]
9
Array["22", ....]

編寫程式獲取正則捕獲的所有內容(正則一定不要忘記加g)
var reg = /\d+/g;
var str = "lemon2020age22";
var ary = [ ];
var res = reg.exec(str);
while(res) {
    ary.push(res[0]);
    res = reg.exec(str);
}
console.log(ary)

["2020" , "22"]

解決正則貪婪性

只需要在量詞元字元後面新增一個?即可

var reg = /\d+?/g;
var str = "lemon2020age22";
console.log(reg.exec(str));

["2", ....]

?在正則中有很多作用

  • 量詞, 放在一個普通的元字元後面代表出現0-1次 /\d?/
  • 取消捕獲時候的貪婪, 放在一個量詞的元字元後面是取消捕獲時候的貪婪性
  • ?: , 在分組捕獲時, 只捕獲不分組

match捕獲

字串中的match方法, 把所有和正則匹配的字元都獲取到

var reg = /\d+/g;
var str = "lemon2020age22";
var ary = str.match(reg);
console.log(ary);

["2020", 22]

雖然在當前的情況下match比exec更加的簡便一些, 但是match中存在一些自己處理不了的問題:

在分組捕獲的情況下, match只能捕獲到大正則匹配的內容, 而 對於小正則則捕獲的內容是無法獲取到的 , 多次捕獲到的情況下

分組引用

正則分組:

  • 改變優先順序
  • 分組引用
var reg = /^(\w)\1(\w)\2$/;
console.log(reg.test("zzff"));
console.log(reg.test("z0f_"));

true
false

var reg = /(\w)\1(\w)\2/g;
console.log("zzff1bbzz".match(reg));
console.log("z0f_".match(reg));

\2代表和第二個分組出現一模一樣的內容; \1和第一個分組出現一模一樣的內容;
一模一樣: 和對應的分組中的內容的值都要一樣

分組捕獲

正則在捕獲的時候, 不僅僅把大正則匹配的內容捕獲到, 而且還可以把小分組匹配的內容捕獲到

var reg = /(\d{4})-(\d{2})-(\d{2})/g
console.log(reg.exec('2010-12-22') );

["2010-12-22", "2010", "12", "22"]

非捕獲分組

var reg1 = /(?:\d{4})-(\d{2})-(\d{2})/g
reg1.exec('2010-12-22') 

[ "2010-12-22", "12", "22" ]

在反向引用時,還是通過\num的形式獲取前面分組的匹配結果,只不過 在計算第幾個分組時不包括非捕獲分組

var reg2 = /(?:\d{4})-(\d{2})-(\d{2})-\2/g
reg2.exec('2010-12-22-22')

[ "2010-12-22-22", "12", "22" ]

replace

replace: 把原有的字元替換成新的字元
在不使用正則的情況下, 每當執行一次只能替換一個字元.

var str = "lemon2020lemon2021"
str = str.replace("lemon", "lemonX");
console.log(str)
str = str.replace("lemon", "lemonX")
console.log(str)

"lemonX2020lemon2021"
"lemonXX2020lemon2021"

沒有實現需求

使用正則

var str = "lemon2020lemon2021"
str = str.replace(/lemon/g, "lemonX");

"lemonX2020lemonX2021"

replace第一項的值是一個正則它的實現原理

  1. 首先, 我們和exec捕獲一樣, 把所有和我們正則匹配的都捕獲到,
  2. 然後, 把捕獲的內容替換成新內容

/lemon/g, 按照這個正則把str中所有可以匹配的都捕獲到, 然後統一都替換成我們的"lemonX"

str = str.replace(/lemon/g, function(){
    console.log("ok");
    console.log(arguments);
    return "lemonX"
})

ok

Arguments { 0: "lemon", 1: 0, 2: "lemonX1XX2020lemonX2XX2021", … }
ok

Arguments { 0: "lemon", 1: 13, 2: "lemonX1XX2020lemonX2XX2021", … }

"lemonXX1XX2020lemonXX2XX2021"

var reg2 = /(?:\d{4})-(\d{2})-(\d{2})-\2/g
var str2 = '2010-12-22-22 2013-09-11-11'
str2.replace(reg2, function(){
    console.log("ok");
    console.log(arguments);
    return "1"
})

ok

Arguments { 0: "2010-12-22-22", 1: "12", 2: "22", 3: 0, 4: "2010-12-22-22 2013-09-11-11", … }

ok

Arguments { 0: "2013-09-11-11", 1: "09", 2: "11", 3: 14, 4: "2010-12-22-22 2013-09-11-11", … }

"1 1"

第二個引數換成一個函式

  1. 匿名函式執行多少次, 取決於正則能在字串中捕獲多少次
  2. 每一次執行匿名函式, 裡面傳遞的引數值arguments和我們自己通過exec捕獲到的結果是非常類似的(即使正則有分組, 我們同樣可以通過arguments獲取到分組捕獲到內容)
  3. return 你返回的結果是啥, 就相當於把當前這一次大正則捕獲的內容替換成你返回的內容
var str = "2020-9-11";
var ary = ["零", "壹", "貳", "叄", "肆", "伍", "陸" ,"柒", "捌", "玖"]
str.replace(/(\d)/g, function(){
    return ary[arguments[1]]
})

"貳零貳零-玖-壹壹"

arguments[0]: 大正則捕獲的
arguments[1]: 小正則捕獲的

例項

時間字串格式化

var str = "2020-9-11 18:15:00";
var str2 = "2020/9/11 18:16:00"
var resStr = "{0}年{1}月{2}日 {3}時{4}分{5}秒"
var dataArray = str.match(/\d+/g)
resStr.replace(/{(\d)}/g, function(){
    return  dataArray[arguments[1]];
})
var dataArray2 = str2.match(/\d+/g)
resStr.replace(/{(\d)}/g, function(){
    return  dataArray2[arguments[1]];
})

千分符

9335673817 9, 335, 673, 817

var str4 = "9335673817";
var reg4 = /^(\d{1,3})((?:\d{3})+)$/
str4.replace(reg4, function(){
    var result1 = arguments[1];
    var result2 = arguments[2];
    return result1 + "," + result2.replace(/\d{3}(?!$)/g, function(){
        return arguments[0] + ","
    })
})

9, 335, 673, 817

如果字串的長度-索引位置本身-1模3 === 0, 則在這個字串的後面加一個,
(?!$), 表示不給最後的數字加逗號

var reg = /\d(?!$)/g;
var str = "175335673817"
str.replace(reg, function(r, i){
    if( (str.length - i - 1)%3 ===0)
        return r + ",";
    else
        return r
})

正向預查和負向預查