iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 24
1

在我的經驗裡看到正規表達式最多的地方是在做表單檢驗的時候,對於那串複雜的符號和奇怪的規則,我總是看過就忘記。事實上不用正規表達式,也是能用其他的方法寫出檢查的規則,只是就要寫大量的程式碼來判斷各種狀況,用正規表達式的話可以很簡潔優雅的處理同樣的事情。

簡單來說,正規表達式是在一個字串裡尋找特定模式的文字,比如說你在文字編輯器裡打開搜尋功能,尋找特定的文字,正規表達式就是這樣,但它的能力可以實現複雜的搜尋規則,也可以替換文字。

建立正規表達式

建立正規表達式的方法有二個。第一個是用正規表達式實值,要在頭尾加上斜線/,被二條斜線包起來的這組值是正規表達式,就像我們用引號包住文字代表字串一樣。

例如我們要找到文字當中完全匹配"test"的字串:

const pattern = /test/;

第二個是使用 RegExp建構器函式,將規則以字串方式代入參數,這個方法適用在。

const pattern = new RegExp("test");

二種方式都會建立出一個相同的正規表達式,並存放在pattern這個變數上。

如果我們可以很確定正規表達式的內容,使用正規表達式實值比較好。

詞?與運算子

正規表達式是以詞?及運算子組合而成。詞?(term)是我們要搜尋的文字,比如說我們要找 abc的話,那abc就是詞?。運算子(operator)是正規表達式定義的一些符號,各代表某種規則,如同數學裡的+ - * %符號,二者搭配使用可以建立各種規則。

相同的規則可以用不一樣的正規表達方式撰寫。

完全符合

const pattern = /best/;

"the best" // 1 match
"the beast" // no match

字串要完全符合正規表達式中的詞?,例如正規表達式/best/,在字串the best可以匹配成功,在字串the beast就無法匹配成功。

匹配字元集合

上面的方法跟使用String.includes()沒什麼兩樣,正規表達式的能力當然不止如此。

有時我們有一些候選對象,只需要匹配其中一個就可以。正規表達式裡可以使用一對中括號[]

const pattern = /[abc]/;

"hello world" // no match
"hello world again" // 2 matches (2個a)

如果在中括號字?的左邊加上^符號,表示要匹配除了字?以外的所有字元,不止英文字母,數字、符號、空格也會算進去。

const pattern = /[^abc]/;

"hello world" // 11 matches (10個字母加1個空格)
"hello world again" // 15 matches (13個字母加2個空格)

如果要匹配的字?是連續的資料,與其寫[12345],正規表達式可以用連接線將頭尾之間(包含頭尾)的字元納入這個集合裡[1-5]

const pattern = /[a-e]/;

"hello world" // 2 matches (e, d)

跳脫

我們在寫字串時,如果剛好遇到字串中有單引號或雙引號,跟我們建立字串的符號一樣,我們會用反斜線\在字串的符號前面,代表這是字串的一部份,不是字串的結尾,反斜線的功能就是讓符號「跳脫(escape)」設定的功能,回歸普通字串。

反斜線在正規表達式當中也具有同樣功能,如果我們的詞?裡有上面的[, ], -, ^符號,就需要在前面加上反斜線讓它們變成匹配用的詞?。

如果詞?中有反斜線的話,就要連續用二條反斜線\\來代表一個反斜線字元。

開始與結束

如果我們想確保字串的開頭符合某種模式,就在正規表達式的開頭加上^符號,這裡沒有中括號,所以和上面「匹配字元集合」的地方是同一個符號不同功能。

const pattern = /^he/;

"hello world" // 1 match
"lorem ipsum" // no match

如果是需要結尾匹配的話,在正規表達式的最後加上$符號。

const pattern = /sum$/;

"hello world" // no match
"lorem ipsum" // 1 match

如果同時使用^$,表示字串要完全符合正規表達式。

const pattern = /^world$/

"hello world" // no match
"world" // 1 match

重複符合

如果要尋找連續出現的同一個字元,正規表達式提供了一些規則讓我們能指定不同的重複條件。

在字元後面加上?,表示該字元可以出現一次,或完全不出現。

const pattern = /t?est/;

"test" // 1 match
"est" // 1 match
"ttest" // no match

在字元後面加上+,表示該字元應該至少出現一次。

const pattern = /t+est/;

"test" // 1 match
"est" // no match
"ttest" // 1 match
"tttest" // 1 match

在字元後面加上*,表示該字元可以出現零次、一次,或多次。

const pattern = /t*est/;

"test" // 1 match
"est" // 1 match
"ttest" // 1 match
"tttest" // 1 match

字元後面加上一組大括號,裡面的數字代表字元指定重複的次數。

const pattern = /o{4}/;

"noob" // no match
"noooob" // 1 match
"noooooooob" // no match

在大括號裡面用逗號隔開二個數字,代表重複次數的範圍。

const pattern = /o{2,4}/;

"dog" // no match
"noob" // 1 match
"nooob" // 1 match
"noooob" // 1 match
"noooooooob" // no match

如果省略第二個數字,會變成開放式的範圍,也就是數字以上的重複次數都算匹配。

const pattern = /o{2,}/;

"dog" // no match
"noob" // 1 match
"noooob" // 1 match
"noooooooob" // 1 match

正規表達式在匹配上面這些重複符合有分以「貪婪(greedy)」或「不貪婪(nongreedy/lazy)」二種方式,預設會以 greedy 的方式匹配字串,也就是會回傳長度最長的匹配結果。如果在運算子後面加上?符號,那麼就會以 lazy 的方式,回傳足夠符合正規表達式的匹配結果。

// 字串裡有 hel 三個字元,最後的 l 至少出現一次
const greedyPattern = /hel+/;
const lazyPattern = /hel+?/;

"hello" 
// greedyPattern: "hell"
// lazyPattern: "hel"

上面範例當中,我們要找hel+的正規表達式,表示hel, hell,helll,以及不管最後的l重複多少次都算匹配。而貪婪的匹配方式會回傳最長的結果,不貪婪的方式回傳足夠的結果,就產生了同樣”hello字串卻得到"hell""hel"的差別。

預定義字元集合

有時我們想要匹配的字元是無法用文字表示,像是 tab 鍵和換行鍵這類。JavaScript 在正規表達式語法裡就為我們定義了一些詞?,讓我們能控制這些字元。這邊列出常見的:

  • \d:十進位數字,等於[0-9]
  • \D:十進位數字以外的其他字元,等於[^0-9]
  • \s:任何空白(空格、tab 及換頁等等)
  • \S:空白字元之外的其他字元
  • \w:任何字母與數字字元,包括下底線,等於[A-Za-z0-9_]
  • \W:字母、數字與下底線之外的其他字元,等於[^A-Za-z0-9_]

分組

在數學運算式中我們會用括號()表示裡面的表達式是自成一組,正規表達式也可以用同樣的方式。

// 匹配 pika, pikaa, pikaaa....
const pattern1 = /pika+/;
// 匹配 pika, pikapika, pikapikapika....
const pattern2 = /(pika)+/;

"pikapikachu"
// pattern1: 2 matches ("pika", "pika")
// pattern2: 1 match ("pikapika")

多重選項

我們可以用|符號來表示「或」這個運算。例如/a|b/會符合a或是b字元,/(ab)|(cd)/符合abcd字串。

最後來看一下正規表達式的五個 flags。這些 flags 是加在正規表達式斜線的後面。

  • i—讓正規表達式不區分英文大小寫,所以不只test會符合/test/i,也包括Test, TESTtEsT等等。
  • g—匹配所有符合樣式的地方,和預設的區域匹配不同,區域匹配只會匹配到第一個符合樣式的地方。
  • m—允許對多行文字做匹配,例如對網頁上的多行文字區取值。
  • y—進行 sticky matching。一個對字串做 sticky matching 的正規表達式,只會在最後一次符合處之後進行匹配。
  • u—對 Unicode point 做跳脫。

上一篇
Day 23: Set
下一篇
Day 25: 正規表達式實做
系列文
JavaScript 忍者的修練--從下忍進階到中忍30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言