1. 程式人生 > 其它 >從零開始學正則(二)

從零開始學正則(二)

技術標籤:正則正則表示式

故心故心故心故心小故衝啊


文章目錄



從零開始學正則(二),正則表示式位置匹配攻略

1 .正則表示式位置匹配攻略

正則表示式是匹配模式,要麼匹配字元,要麼匹配位置。請記住這句話。

然而大部分人學習正則時,對於匹配位置的重視程度沒有那麼高。
本章講講正則匹配位置的相關知識點。
內容包括:
什麼是位置?
如何匹配位置?
位置的特性
幾個應用例項分析

1.1. 什麼是位置呢?

位置(錨)是相鄰字元之間的位置。比如,下圖中箭頭所指的地方:
在這裡插入圖片描述

1.2. 如何匹配位置呢?

在 ES5 中,共有 6 個錨: ^、$、\b、\B、(?=p)、(?!p)

1.2.1. ^ 和 $

^(脫字元)匹配開頭,在多行匹配中匹配行開頭。
$(美元符號)匹配結尾,在多行匹配中匹配行結尾。
比如我們把字串的開頭和結尾用 “#” 替換(位置可以替換成字元的!):

var result = "guxin"
.replace(/^|$/g, '#'); console.log(result); // => "#guxin#

多行匹配模式(即有修飾符 m)時,二者是行的概念,這一點需要我們注意:

var result = "guxin\nguxinxin\nguxinya".replace(/^|$/gm, '#');
console.log(result);
/*
#guxin#
#guxinxin#
#guxinya#
*/

1.2.2. \b 和 \B

\b 是單詞邊界,具體就是 \w 與 \W 之間的位置,也包括 \w 與 ^ 之間的位置,和 \w 與 $ 之間的位置。

var result = "[I] am guxin xi_xixi".replace(/\b/g, '#');
console.log(result);
// => "[#I#] #am# #guxin# #xi_xixi#"

\B 就是 \b 的反面的意思,非單詞邊界。例如在字串中所有位置中,扣掉 \b,剩下的都是 \B 的。
具體說來就是 \w 與 \w、 \W 與 \W、^ 與 \W,\W 與 $ 之間的位置。

var result = "[I] am guxin xi_xixi".replace(/\B/g, '#');
console.log(result);
// => "#[I]# a#m g#u#x#i#n x#i#_#x#i#x#i"

1.2.3. (?=p) 和 (?!p)

(?=p),其中 p 是一個子模式,即 p 前面的位置,或者說,該位置後面的字元要匹配 p。
比如 (?=l),表示 “l” 字元前面的位置,例如

var result = "helloguxin".replace(/(?=guxin)/g, '#');
console.log(result);
// => "hello#guxin

而 (?!p) 就是 (?=p) 的反面意思,比如:

//不在guxin前面加,其他之間加
var result = "helloguxin".replace(/(?!guxin)/g, '#');
console.log(result);
// => "#h#e#l#l#og#u#x#i#n#"

1.3. 位置的特性

對於位置的理解,我們可以理解成空字元 “”。
比如 “hello” 字串等價於如下的形式:

"hello" == "" + "h" + "" + "e" + "" + "l" + "" + "l" + "" + "o" + "";

也等價於:

"hello" == "" + "" + "hello"

因此,把 /^helloKaTeX parse error: Expected group after '^' at position 7: / 寫成 /^̲^hello$$/,是沒有任何問題的:

var result = /^^hello$$$/.test("hello");
console.log(result);
// => true
var result = /(?=he)^^he(?=\w)llo$\b\b$/.test("hello");
console.log(result);
// => true

也就是說字元之間的位置,可以寫成多個。

1.4. 相關案例

1.4.1. 不匹配任何東西的正則

讓你寫個正則不匹配任何東西

easy,/.^/

因為此正則要求只有一個字元,但該字元後面是開頭,而這樣的字串是不存在的

1.4.2 數字的千位分隔符表示法

比如把 “12345678”,變成 “12,345,678”。
可見是需要把相應的位置替換成 “,”。
思路是什麼呢?

首先弄出最後一個逗號
使用 (?=\d{3}$) 就可以做到:

var result = "12345678".replace(/(?=\d{3}$)/g, ',')
console.log(result);
// => "12345,678

其中,(?=\d{3}KaTeX parse error: Undefined control sequence: \d at position 6: ) 匹配 \̲d̲{3} 前面的位置。而 \d{3}$ 匹配的是目標字串最後那 3 位數字。

然後弄出所有的逗號
因為逗號出現的位置,要求後面 3 個數字一組,也就是 \d{3} 至少出現一次。
此時可以使用量詞 +:

var result = "12345678".replace(/(?=(\d{3})+$)/g, ',')
console.log(result);
// => "12,345,678"

1.4.3. 匹配其餘案例

寫完正則後,要多驗證幾個案例,此時我們會發現問題:

var result = "123456789".replace(/(?=(\d{3})+$)/g, ',')
console.log(result);
// => ",123,456,789"

因為上面的正則,僅僅表示把從結尾向前數,一但是 3 的倍數,就把其前面的位置替換成逗號。因此才會出
現這個問題。
怎麼解決呢?我們要求匹配的到這個位置不能是開頭。 我們知道匹配開頭可以使用 ^,但要求這個位置不是開頭怎麼辦?
easy,(?!^),你想到了嗎?測試如下:

var regex = /(?!^)(?=(\d{3})+$)/g;
var result = "12345678".replace(regex, ',')
console.log(result);
// => "12,345,678"
result = "123456789".replace(regex, ',');
console.log(result);
// => "123,456,789"

1.4.4支援其他形式

如果要把 “12345678 123456789” 替換成 “12,345,678 123,456,789”。
此時我們需要修改正則,把裡面的開頭 ^ 和結尾 $,修改成 \b:

//不匹配單詞邊界的
var string = "12345678 123456789",
regex = /(?!\b)(?=(\d{3})+\b)/g;
var result = string.replace(regex, ',')
console.log(result);
// => "12,345,678 123,456,789

其中 (?!\b) 怎麼理解呢?
要求當前是一個位置,但不是 \b 前面的位置,其實 (?!\b) 說的就是 \B。 因此最終正則變成了:/\B(?=(\d{3})+\b)/g。

1.4.5驗證密碼問題

密碼長度 6-12 位,由數字、小寫字元和大寫字母組成,但必須至少包括 2 種字元。
此題,如果寫成多個正則來判斷,比較容易。但要寫成一個正則就比較困難。 那麼,我們就來挑戰一下。看看我們對位置的理解是否深刻。

如果不考慮“但必須至少包括 2 種字元”這一條件。我們可以容易寫出:

var regex = /^[0-9A-Za-z]{6,12}$/;

判斷是否包含有某一種字元
假設,要求的必須包含數字,怎麼辦?此時我們可以使用 (?=.*[0-9]) 來做。 因此正則變成:

var regex = /(?=.*[0-9])^[0-9A-Za-z]{6,12}$/;

同時包含具體兩種字元
比如同時包含數字和小寫字母,可以用 (?=.[0-9])(?=.[a-z]) 來做。 因此正則變成:

var regex = /(?=.*[0-9])(?=.*[a-z])^[0-9A-Za-z]{6,12}$/;

解惑

上面的正則看起來比較複雜,只要理解了第二步,其餘就全部理解了。
/(?=.*[0-9])^[0-9A-Za-z]{6,12}$/
對於這個正則,我們只需要弄明白 (?=.*[0-9])^ 即可。

分開來看就是 (?=.*[0-9])^表示開頭前面還有個位置(當然也是開頭,即同一個位置,想想之前的空字元類比)。
(?=.*[0-9]) 表示該位置後面的字元匹配 .*[0-9],即,有任何多個任意字元,後面再跟個數字。
翻譯成大白話,就是接下來的字元,必須包含個數字。