0%

【學習筆記】JavaScript - Regex 正則表達式

Why we need Regular Expression?

正則表達式(Regular Expression),常簡寫為 RegEx、RegExp 或 RE,代表描述一種字串匹配的模式(pattern)。在程式語言中,通常用來搜尋、比對、替換符合某個模式的文字。

在 JavaScript 中,Regex 常見使用情境如下:

  1. 尋找符合條件的字串
  2. 取代符合條件的字串
  3. 驗證使用者輸入欄位

舉個簡單的例子:「如何判斷某段資料內,是否包含字元 H?」

可能就有以下三種解法:

let str = 'Hello World';

// 法一:跑迴圈
for (let i = 0; i < str.length - 1; i++) {
  if (str[i] === 'H') {
    return true; 
  }
}

// 法二:使用 indexOf
if (str.indexOf('H') !== -1) {
  return true;
}

// 法三:正則表達式
if (new RegExp('H').test(str)) {
  return true;
}

How to use?

撰寫正規表達式,可透過以下兩種方式:

  • 正規表達式字面值(literal):使用兩個斜線 / /
    • 在 script 載入時被編譯,效能較佳
let reStr = /abc/
  • 建構式:直接 new 一個 RegExp 物件 new RegExp()
    • 適合需動態產生 pattern 時使用
let reStr = new RegExp('abc')       // 查詢第一次匹配項目

特殊字元

在正規表達式中,某些特殊字元或符號屬於保留字:

以下為使用範例:

const str = 'The History of Hello World History'

str.match(/History/)    // ['History', index: 4, ...
// ^:匹配輸入的開頭
str.match(/^History/)   // null
// $:匹配輸入的結尾
str.match(/History$/)   // ['History', index: 27...

// 使用反斜線 '\' 來跳脫特殊字元
const reg = /\$6/
reg.test('$666')         // ture

匹配次數:*+?{}

預設情況下,一個字元只會匹配一次,搭配 *+?{} 字元可指定匹配次數:

  • *:任意次數,等同於 {0,}
  • +:至少一次(後面要跟著),等同於 {1,}
  • ?:零或一次(有或沒有),等同於 {0,1}
  • {m}:m 次
  • {m, n}:從 m 到 n 次
const str = 'banana'

const regex = /ban/       // 符合 ban
const regex = /ba*n/      // ba, ban, baaan 均符合
const regex = /k?a/       // k 可有可無
const regex = /a+n/       // 必須包含 an
const regex = /an{2}/     // an 要包含 2 次
const regex = /an{2,4}/   // an 介於 2 到 4 次
const regex = /an{2,}/    // 2 次以上 an 均符合

標記 Flag:igm

igm 這類標記(flag),不會寫在正則表達式裡,格式與範例如下:

/pattern/flags

 let reStr = /abc/i                   // ignore:不區分大小寫
 let reStr = new RegExp('abc', 'g')   // global:全局匹配   

可參考下圖,在 Regexr 網站的測試結果:


常用語法

在 JavaScript 中,常與 RegExp 物件搭配使用的函式如下:

  • 檢測是否包含指定字串:test()search()
  • 尋找並回傳指定字串:exec()match()
  • 取代指定字串:replace()

RegExp.prototype.test():檢測字串是否包含,回傳 true/false

  • 類似 String.prototype.search() 方法,差別在於回傳值不同
const paragraph = 'Hello World';
const regex1 = /^He/g;
const regex2 = /Woooo/g;

console.log(regex1.test(paragraph))    // true
console.log(regex2.test(paragraph))    // false

String.prototype.search():檢測字串是否包含,有的話回傳 index,否則回傳 -1

const paragraph = 'Hello World';
const regex1 = /^He/g;
const regex2 = /Woooo/g;

console.log(paragraph.search(regex1))    // 0
console.log(paragraph.search(regex2))    // -1

RegExp.prototype.exec():尋找並取出內容,有的話以陣列回傳,否則回傳 null

  • 類似 String.prototype.match() 方法,差別在於「全域性匹配」時結果不同
const paragraph = 'Hello, I am fine. Thank you.';
const regex = /[A-Z]/;
const globalReg = /[A-Z]/g;

console.log(regex.exec(paragraph));       // ["H"]
console.log(globalReg.exec(paragraph));   // ["H"]

String.prototype.match():尋找並取出內容,有的話以陣列回傳,否則回傳 null

const paragraph = 'Hello, I am fine. Thank you.';
const regex = /[A-Z]/;
const globalReg = /[A-Z]/g;

console.log(paragraph.match(regex));      // ["H"]
console.log(paragraph.match(globalReg));  // ["H", "I", "T"]

String.prototype.replace():尋找並取代字串中匹配的部分

const str = 'banana'

console.log(str.replace(/na/, 'NA'));   // baNAna
console.log(str.replace(/n./, 'NA'));   // baNAna  '.' 代表任意字元
console.log(str.replace(/na/g, 'NA'));  // baNANA

常用匹配規則

詳細規則可參考這篇:正則表示式 – 匹配規則

[a-z]           // 匹配所有的小寫字母 
[A-Z]           // 匹配所有的大寫字母 
[a-zA-Z]        // 匹配所有的字母 
[0-9]           // 匹配所有的數字 
[0-9\.\-]       // 匹配所有的數字,句號和減號 

^[a-z][0-9]$    // 匹配一個開頭為小寫字母,和一位數字組成的字串:e7
^[A-Z][0-9]{9}  // 身分證字號(大寫英文 + 9 位數字):A123456789
[0-9]{10}       // 手機號碼(10 位數字):0912345678
/\d{6}/         // 匹配 6 次任意數字:123456

參考資料

  • MDN - 正規表達式- JavaScript
  • 十五分鐘認識正規表達式,解決所有文字難題
  • [Javascript] 初探Regex 正規表達式
  • [JS] 正則表達式(Regular Expression, regex
  • 測試網站:Regexr