軌道
/
JavaScript
JavaScript
/
課程大綱
/
正規表示式
正規

正規表示式 在 JavaScript

9 個練習

關於 正規表示式

JavaScript 中的正規表達式

正規表達式(或稱 Regex)是一連串的字元,我們可以用來鎖定並操作字串中的特定元素。因此,我們可以:

  • 搜尋字串中的文字
  • 取代字串中的子字串
  • 從字串中擷取資訊

這個概念著重於在 JavaScript 中使用正規表達式。如果你想了解如何撰寫正規表達式,Eloquent JavaScript 可能是個不錯的資源。

💡 JavaScript 的正規表達式方言是該語言 ECMA-262 標準的一部分。這表示你不必擔心瀏覽器專屬的相容性問題。

如何建立正規表達式

在 JavaScript 中,正規表達式大多以 /pattern/modifiers 的格式撰寫,其中「pattern」是主要的正規表達式,而「modifiers」則是一連串字元,我們可以加上它們來表示特殊選項(就像是超能力一樣)。

不過,我們有兩種建立正規表達式的方式:

  1. 正規表達式實字:
const regex = /[a-z]/;
  1. 建構函式 RegExp:
const regex = new RegExp('[a-z]');

在這兩種情況下,JavaScript 都會用正規表達式建立出一個物件。建議預設採用不可變的模式,也就是實字寫法。

當正規表達式會變動,或我們還不知道它會是什麼(例如來自輸入的內容)時,就可以使用 RegExp 建構函式。

🏴‍☠️ 旗標

正規表達式有一些選用的超能力,稱為 flags,能提供額外的功能。這些旗標可以個別使用,也能以任意順序組合使用,而且都會成為正規表達式的一部分。

一些廣泛使用的旗標包括:

  • /g - 全域搜尋
  • /i - 不分大小寫
  • /m - 多行搜尋

這裡有個簡單的例子:

const re = /home/gi;
const str = 'Home, sweet home.';

str.match(re);

// => ["Home", "home"]

g 字元讓我們能解析字串中所有可能的匹配。如果沒有這個功能,JavaScript 只會擷取出第一個 Home 匹配。

不分大小寫旗標 /i 讓我們在尋找模式時更有彈性。如此一來,我們要找的內容是大寫的 UPPERCASE 還是小寫的 lowercase 都無所謂。

使用 RegExp 建構函式時,加上旗標的語法不一樣。在這種情況下,我們只要把旗標當作第二個引數傳入即可。

常用的函式

當正規表達式與 JavaScript 目前內建的函式結合時,我們就有了一些強大的方式來操作字串,並從中取得資料。

test() 方法是很棒的方式,能搜尋並得知目標值是否存在於指定的字串中。如此一來,它會回傳一個布林值,true 或 false。

match() 方法會從指定的字串中擷取正規表達式的匹配,並回傳一個包含資訊與匹配結果的陣列。

什麼時候該用哪一個? 當你想要快速檢查字串中是否有某個值時,就用 test();如果你需要使用那個值,或想取得更多關於匹配的資訊,就用 match()。

Test

test() 方法會對正規表達式與指定字串之間的匹配執行搜尋,並回傳 true 或 false。

const str = 'It is difficult to test if you have a virus';
const result = /virus$/.test(str);

console.log(result);

// => true

Match

不過,如果我們想取得正規表達式搜尋的實際值呢?

使用 match() 方法時,我們不會只得到一個布林值,而是會取得一個實用的陣列,其內容取決於找到的匹配是否存在。

如此一來,我們既能搜尋,也能從任何字串中擷取資訊。例如:

const funnyQuote =
  'If you see someone crying, ask if it is because of their haircut.';
const regex1 = /someone/;
const regex2 = /happy/;

funnyQuote.match(regex1);
// => ["someone", index: 3, input: "If you see someone crying, ask if it is because of their haircut.", groups: undefined]

funnyQuote.match(regex2);
// => null

當全域搜尋旗標 /g 存在時,這個方法不會只取得唯一的匹配及索引或輸入等實用資訊,而是會回傳陣列中出現的所有結果:

const funnyQuote =
  'If you see someone crying, ask if it is because of their haircut.';

const regex3 = /if/gi;

funnyQuote.match(regex3);
// => ["If", "if"];

Replace

JavaScript 中的 replace() 方法讓我們能在指定的字串中搜尋某個值,並將它取代為新的值。

string.replace(searchValue, newValue);

用來搜尋取代項目的模式可以是單一字串,或是一個正規表達式。

let string = 'I like dogs!';
let result = string.replace('dogs', 'cats');

let string = 'I would love to travel to Japan';
let result = string.replace(/Japan/g, 'Hawaii');

此外,我們可以在取代的位置套用一個函式,以便對每個值做額外的修改。

let text = 'Say hello to the chatbot.';
let result = text.replace(/chatbot|hello/gi, function (word) {
  return word.toUpperCase();
});
// => "Say HELLO to the CHATBOT"

💡 總而言之,正規表達式與 replace() 方法的組合,是取代元素時更有彈性的方式。只使用單一字串可能會有所限制。

Split

JavaScript 中的 split() 方法,代表著另一種搭配正規表達式使用與操作字串的方式。

如此一來,我們會使用正規表達式,透過辨識某個模式來切分指定的字串,例如 str.split(/[,.\s]/)。這個模式會用來當作 separator。

const str = 'hello,user.how are.you';

const result = str.split(/[,.\s]/);

console.log(result);
// => ['hello', 'user', 'how', 'are', 'you']

效能

就效能而言,兩者都會建立一個 RegExp 物件。主要的差異在於正規表達式被編譯的頻率:

  • 使用 Regular Expression Literal:在程式碼初始解析與編譯時編譯一次
  • 使用 RegExp() 語法:每次建立新物件時都會編譯。

使用實字語法可能是更好的選擇,不只是因為效能,也因為簡單與可讀性。更多細節請參考這篇 Stackoverflow 討論。

  1. 它比較短,也不會強迫你以類別式建構函式的思維來思考。
  2. 使用 RegExp() 建構函式時,你還需要跳脫引號,並將反斜線雙重跳脫。這會讓本質上就難以閱讀與理解的正規表達式變得更加困難。
透過 GitHub 編輯 連結會在新視窗或分頁中開啟

學習 正規表示式