本系列內容是基於 Eloquent JavaScript 4th edition (2024) 所整理的精簡筆記。
Some people, when confronted with a problem, think ‘I know, I’ll use regular expressions.’ Now they have two problems.── Jamie Zawinski
本文深入探討 JavaScript 中的正則表達式機制,從基礎的字元集合定義,到進階的匹配策略。透過實際的程式碼範例,說明如何運用重複模式、分組匹配、貪婪匹配等重要特性來提升文字處理的效率和靈活性。同時也介紹了 JavaScript 特有的正則表達式方法,以及 INI 檔案解析實際應用場景的重要實踐。
正則表達式(Regular Expression)
1. 什麼是正則表達式?
正則表達式(Regular Expression)是一種描述文字模式的語法,用於在字串中搜尋、匹配和操作文字。它由一個或多個字元組成,可以表示特定的文字模式,如單一字元、字元集合、字元範圍、可選字元、重複字元等。正則表達式常被用於文字處理、資料驗證、字串操作等場景。
2. 如何創建正則表達式?
let re1 = new RegExp("abc");
let re2 = /abc/;
- RegExp 構造函數
- 字面量表示法
javascript
let aPlus = /A\+/; // 表示字元本身,需在字元前加入反斜線\
3. 測試匹配(Test Method)
正則表達式的 test
方法用於檢查字串中是否包含與正則表達式相匹配的子字串,並返回布林值。如果找到匹配,則返回 true
,否則返回 false
。
console.log(/abc/.test("abcde"));
// → true
console.log(/abc/.test("abxde"));
// → false
字元集合(Character Classes)
1. 什麼是字元集合?
正則表達式中的字元集合用於匹配特定範圍或集合的字元。它們通常用方括號 []
包裹,並包含要匹配的字元或範圍。
2. 如何使用字元集合?
字元集合可以包含單個字元或字元範圍。範圍使用連字元 -
表示,例如 [0-9]
表示所有數字字元(Unicode 編碼 48~57)。範圍的排序由字元的 Unicode 編碼決定。以下兩個表達式都匹配包含數字的所有字串:
console.log(/[0123456789]/.test("in 1992"));
// → true
console.log(/[0-9]/.test("in 1992"));
// → true
3. 特殊字元集合
正則表達式中有一些特殊字元集合,用於匹配特定類型的字元,只有反斜線代碼可用在方括號 []
中。例如:
\d
:任何數字字元(等同於[0-9]
)\w
:任何單詞字元(字母、數字或下劃線)\s
:任何空白字元(空格、制表符、換行字元等)\D
:任何非數字字元(等同於[^0-9]
)\W
:任何非單詞字元(等同於[^a-zA-Z0-9_]
)\S
:任何非空白字元(等同於[^ \t\n\r\f]
).
:任何單個字元(除了換行字元\n
),但在方括號[]
內表示字面上的句點。javascriptlet dot = /./; console.log(dot.test("bearune")); // → true let dotInBracket = /[.]/; console.log(dotInBracket.test("bearune")); // → false console.log(dotInBracket.test("bearune.")); // → true
範例
我們可以使用以下表達式匹配 01-30-2003 15:20
格式的日期和時間:
let dateTime = /\d\d-\d\d-\d\d\d\d \d\d:\d\d/;
console.log(dateTime.test("01-30-2003 15:20"));
// → true
console.log(dateTime.test("30-jan-2003 15:20"));
// → false
乍看之下 \d
非常多且不易讀,但其實這個範例有更好的表示方法,下面重複模式會提到。
4. 反轉字元
如果在方括號 []
中使用 ^
字元,表示反轉字元集合,即匹配不在方括號內的字元。例如,[^01]
表示匹配任何 0 與 1 以外的字元。
let nonBinary = /[^01]/;
console.log(nonBinary.test("1100100010100110"));
// → false
console.log(nonBinary.test("0111010112101001"));
// → true
5. 國際字元
由於 JavaScript 當初並沒有考慮到正則表達式的國際化需求,因此在 Unicode 字元上存在一些問題。譬如 \w
表示單詞字元,但不包括非拉丁字母的字元,如日文、中文等。
為了解決這個問題,ES9 引入了 \p{}
和 \P{}
來表示 Unicode 屬性,並且因為兼容性問題,需在正則表達式後添加 u
字元。
\p{}
:匹配具有指定 Unicode 屬性的字元\P{}
:匹配不具有指定 Unicode 屬性的字元
下例使用 \p{Script=Han}
表示匹配所有漢字字元。
let han = /\p{Script=Han}/u;
console.log(han.test("熊途"));
// → true
let notHan = /\P{Script=Han}/u;
console.log(notHan.test("熊途"));
// → false
重複模式(Repetition)
1. 什麼是重複模式?
重複模式用於指定匹配字元或字元集合的次數。例如:我們想要匹配一個或多個數字的序列,可以使用 \d+
表示:
console.log(/'\d+'/.test("'123'"));
// → true
console.log(/'\d+'/.test("''"));
// → false
2. 如何使用重複模式?
重複模式可以使用以下符號表示:
+
: 1 次或多次*
: 0 次或多次
// + 與 * 的差異
console.log(/'\d+'/.test("'123'"));
// → true
console.log(/'\d+'/.test("''"));
// → false
console.log(/'\d*'/.test("'123'"));
// → true
console.log(/'\d*'/.test("''"));
// → true
?
: 0 次或 1 次
// u 字元可以出現也可以缺失
let neighbor = /neighbou?r/;
console.log(neighbor.test("neighbour"));
// → true
console.log(neighbor.test("neighbor"));
// → true
{n}
: 精確重複 n 次{n,m}
: 重複 n 到 m 次{n,}
: 至少重複 n 次
let dateTime = /\d{1,2}-\d{1,2}-\d{4} \d{1,2}:\d{2}/;
console.log(dateTime.test("1-30-2003 8:45"));
// → true
3. 貪婪與非貪婪匹配
3.1 貪婪匹配(Greedy)
預設情況下,重複運算子(+
、*
、?
、{}
)會盡可能匹配最多的字元。以下是一個移除 JavaScript 註釋的例子:
function stripComments(code) {
return code.replace(/\/\/.*|\/\*[^]*\*\//g, "");
}
console.log(stripComments("1 + /* 2 */3"));
// → 1 + 3
console.log(stripComments("x = 10;// ten!"));
// → x = 10;
console.log(stripComments("1 /* a */+/* b */ 1"));
// → 1 1
這個函數的目的很簡單:把程式碼中的註釋都清掉。註釋有兩種:
- 單行註釋:以
//
開頭到行尾的內容 - 多行註釋:被
/*
和*/
包起來的內容
但是最後一個例子出現了奇怪的結果!原本應該是 1 + 1
,卻變成了 1 1
。這是為什麼呢?
問題出在正則表達式太「貪吃」了。當它看到 /* a */+/* b */
這段時,它不是乖乖地分別吃掉兩個註釋,而是整段都吞下去!因為我們用了 [^]*
這個模式,它的意思是「吃掉所有能吃的東西,直到看到最後一個 */
」,具體原理可參考下方回溯機制。
為了解決這個問題,我們就必須使用非貪婪匹配。
3.2 非貪婪匹配(Non-greedy)
如果我們希望重複運算子盡可能匹配最少的字元,可以使用 ?
來表示非貪婪匹配。以下是剛才的例子:
function stripComments(code) {
return code.replace(/\/\/.*|\/\*[^]*?\*\//g, "");
}
console.log(stripComments("1 /* a */+/* b */ 1"));
// → 1 + 1
這樣就能讓 [^]*
匹配到最近的 */
,並輸出預期的結果了。
正則表達式選項
正則表達式可以使用選項來修改其行為。以下是常用的選項:
1. i(ignore case)
i
能使匹配不區分大小寫,如下所示:
let pattern = /hello/i;
console.log(pattern.test("Hello")); // → true
console.log(pattern.test("HELLO")); // → true
2. g(global)
g
為全局匹配,查找所有匹配項而不是只找第一個匹配項,如下所示:
"banana".match(/an/g); // → ['an', 'an']
"banana".replace(/an/g, "AN"); // → "bANANa"
3. y(sticky)
y
為黏性匹配,只從 lastIndex
位置開始匹配,如下所示:
let sticky = /abc/y;
sticky.lastIndex = 3;
console.log(sticky.test("123abc")); // → true
console.log(sticky.test("abc123")); // → false
4. u(unicode)
u
啟用 Unicode 模式,允許正則表達式使用 Unicode 字元,並使 \p{...}
語法生效,如下所示:
console.log(/\p{L}/u.test("α")); // → true
console.log(/\p{Script=Greek}/u.test("α")); // → true
console.log(/🍎{3}/u.test("🍎🍎🍎")); // → true
分組基礎
1. 什麼是分組?
在正則表達式中,括號 ()
用於創建一個分組,使得括號內的模式作為一個整體被處理。這對於以下情況特別有用:
範例:將多個元素作為一個整體應用重複運算子
let cartoonCrying = /boo+(hoo+)+/i;
console.log(cartoonCrying.test("Boohoooohoohooo"));
// → true
- 第一個及第二個
+
分別應用於boo
及hoo
中的o
。 - 第三個
+
應用於整個(hoo+)
組。 - 表達式末尾的
i
使匹配不區分大小寫。
2. 非捕獲分組
如果只想使用括號進行分組,但不希望它出現在匹配結果中,可以使用 (?:)
語法:
// 使用非捕獲分組
console.log(/(?:na)+/.exec("banana"));
// → ["nana"]
// 對比使用捕獲分組
console.log(/(na)+/.exec("banana"));
// → ["nana", "na"]
分組與匹配結果
test
方法只提供「是否匹配」的結果,是正則表達式中最簡單的方法,以下方法提供更詳細的匹配結果,例如:
1. exec 方法
exec
方法是正則表達式的一個強大方法,它返回一個包含匹配信息的物件:
let match = /\d+/.exec("one two 100");
console.log(match);
// → ["100"]
console.log(match.index); // 匹配開始的位置
// → 8
- 如果沒有找到匹配,返回
null
- 如果找到匹配,返回一個類陣列物件(Array-like Object),包含:
- 陣列第一項是完整的匹配字串
- 陣列其他項是各個分組匹配的內容
index
屬性表示匹配開始的位置
1.1 分組匹配
對含有分組匹配的表達式,exec
方法會返回一個包含匹配字串及各個分組匹配內容的陣列,如下所示:
let quotedText = /'([^']*)'/;
- 外層的單引號
'
:匹配的文本必須以單引號開始和結束。 - 捕獲組
([^']*)
這個正則表達式的整體需求是:
它將匹配一段文本,該文本是由單引號 '
包圍的,並且在單引號內可以包含任意數量的非單引號字元。
console.log(quotedText.exec("she said 'hello'"));
// → ["'hello'", "hello"]
1.2 特殊情況
- 當一個分組沒有匹配到內容時(比如使用
?
時),對應位置會是undefined
。 - 當一個分組被多次匹配時(比如使用
+
時),只有最後一次匹配會被記錄。
console.log(/bad(ly)?/.exec("bad"));
// → ["bad", undefined]
console.log(/(\d)+/.exec("123"));
// → ["123", "3"]
1.3 lastIndex
當正則表達式啟用全局(g
)或黏性(y
)標誌時,lastIndex
屬性會影響 exec
方法的行為:
lastIndex
控制下一次匹配的起始位置- 匹配成功時,
lastIndex
自動更新為匹配結束的位置 - 匹配失敗時,
lastIndex
重置為 0
let pattern = /y/g;
pattern.lastIndex = 3;
let match = pattern.exec("xyzzy");
console.log(match.index); // → 4
console.log(pattern.lastIndex); // → 5
注意:在多次使用同一個正則表達式物件時,lastIndex
的自動更新可能導致意外結果:
let digit = /\d/g;
console.log(digit.exec("here it is: 1")); // → ["1"]
console.log(digit.exec("and now: 1")); // → null,因為 lastIndex 沒有重置
因此,當使用全局(g
)或黏性(y
)標誌時,建議在每次使用前根據需要手動重置 lastIndex
為 0
。若只是想找出所有匹配項,可以考慮使用 match
方法(在使用 g
標誌時)或 matchAll
方法,這樣可以避免手動處理 lastIndex
的問題,這兩個方法將在下面介紹。
2. match 方法
字串的 match
方法與正則表達式的 exec
方法類似,但有一些重要區別:
let match = "one two 100 200".match(/\d+/);
console.log(match); // 只匹配第一個匹配的字串,與 exec 方法相同
// → ["100"]
2.1 全局搜索時的行為差異
使用 g
標誌時,match
會返回所有匹配項的數組,而不是像 exec
那樣只返回第一個匹配的詳細信息。
console.log("Banana".match(/an/));
// → ["an"]
console.log("Banana".match(/an/g));
// → ["an", "an"]
console.log(/an/.exec("Banana"));
// → ["an"]
2.2 使用建議
- 如果需要獲取分組信息或精確的匹配位置,使用
exec
。 - 如果只需要獲取所有匹配的字串,使用帶有
g
標誌的match
。
3. matchAll 方法(ES2020+)
如果需要獲取所有匹配的詳細信息,可以使用字串的 matchAll
方法:
let input = "A string with 3 numbers in it... 42 and 88.";
let matches = input.matchAll(/\d+/g);
for (let match of matches) {
console.log("Found", match[0], "at", match.index);
}
// → Found 3 at 14
// → Found 42 at 33
// → Found 88 at 40
- 使用
matchAll
時,正則表達式必須包含g
標誌。
參考資料:String.prototype.matchAll() - JavaScript - MDN Web Docs
4. 比較
方法 | 返回值類型 | 全域搜尋 | 捕獲組處理 | 特點 |
---|---|---|---|---|
exec() | Array 或 null | 需手動迭代 | 完整支援 | - 返回單次匹配結果和詳細資訊 - 保留 lastIndex - 需使用 g 標誌並重複調用才能獲取所有匹配 |
match() | Array 或 null | 自動(有g 時) | 有限支援 | - 無 g :返回首次匹配和捕獲組- 有 g :返回所有匹配但不含捕獲組 |
matchAll() | Iterator | 自動 | 完整支援 | - 返回包含所有匹配資訊的迭代器 - 必須使用 g 標誌- 可用 for...of 遍歷 |
const str = "cat, bat, sat, fat";
const regex = /([a-z])at/g;
// exec() - 需要循環獲取所有匹配
let result;
while ((result = regex.exec(str)) !== null) {
console.log(result);
}
// → ["cat","c"]
// → ["bat","b"]
// → ["sat","s"]
// → ["fat","f"]
// match() - 有 /g 時只返回完整匹配
console.log(str.match(regex));
// → ["cat", "bat", "sat", "fat"]
// matchAll() - 返回完整資訊的迭代器
const matches = [...str.matchAll(regex)];
console.log(matches);
// → [["cat","c"], ["bat","b"], ["sat","s"], ["fat","f"]]
字串方法
以下介紹兩個可以使用正則表達式的字串方法:replace
和 split
。
1. replace 方法
1.1 如何使用 replace 方法?
replace
方法用於在字串中替換匹配的字串。它接受兩個參數:要替換的字串
或正則表達式
,以及替換的內容
。當在正則表達式之後添加 g
選項,還能替換所有匹配項。
let str = "cat, cat, cat";
// 只替換第一個匹配
console.log(str.replace("cat", "dog")); // → "dog, cat, cat"
console.log(str.replace(/cat/, "dog")); // → "dog, cat, cat"
// 使用正則替換所有匹配
console.log(str.replace(/cat/g, "dog")); // → "dog, dog, dog"
1.2 分組匹配
replace
方法還支持分組匹配,允許在替換內容中引用匹配的子字串,如下所示。
console.log(
"Liskov, Barbara\nMcCarthy, John\nMilner, Robin"
.replace(/(\p{L}+), (\p{L}+)/gu, "$2 $1"));
// → "Barbara Liskov
// John McCarthy
// Robin Milner"
替換字元中的 $1
、$2
指的是要替換表達式的分組,表示為 $n
,其中 n
為小於 100 的正整數;$&
則表示整個匹配。
console.log("javascript".replace(/.*/, "$&$&")); // → "javascriptjavascript"
2. search 方法
2.1 如何使用 search 方法?
與字串的 indexOf
相似,用於字串中執行正則表達式的搜索,並返回首次匹配位置;如果未找到匹配,則返回 -1
。
const str = "Hello World";
console.log(str.search(/World/)); // → 6
console.log(str.search(/javascript/)); // → -1
2.2 與 indexOf 的區別
search
方法與 indexOf
的主要區別在於,search
方法接受正則表達式作為參數,而 indexOf
方法則接受字串作為參數。
const str = "Hello World";
// indexOf 只能搜索固定字串
console.log(str.indexOf("World")); // → 6
// search 可以使用正則表達式
console.log(str.search(/W\w+/)); // → 6
邊界和前瞻
下面是一個以字串創建日期的物件,展示如何使用正則表達式來解析日期字串並返回一個 Date
物件:
function getDate(string) {
let [_, month, day, year] =
/(\d{1,2})-(\d{1,2})-(\d{4})/.exec(string);
return new Date(year, month - 1, day);
}
console.log(getDate("1-30-2003"));
// → Thu Jan 30 2003 00:00:00 GMT+0100 (CET)
雖然看似很正常,但 getDate
也能從字串 100-1-30000
中提取日期,這不是我們想要的結果,因此我們需要添加一些額外的限制,而 邊界和前瞻可以幫助我們解決這個問題。
1. 邊界(Boundaries)
邊界是在正則表達式中用來限制匹配模式的位置標記。它們不會匹配任何實際字元,而是強制在模式中出現的位置滿足特定條件。常見的邊界符號包括:
^
:匹配字串開頭javascriptlet startWithNumber = /^\d+/; console.log(startWithNumber.test("123abc")); // → true console.log(startWithNumber.test("abc123")); // → false
$
:匹配字串結尾javascriptlet endWithNumber = /\d+$/; console.log(endWithNumber.test("abc123")); // → true console.log(endWithNumber.test("123abc")); // → false
^
與$
組合:匹配整個字串javascriptlet numbers = /\d+/; // 未使用 ^ 和 $ console.log(numbers.test("123")); // → true console.log(numbers.test("123abc")); // → true console.log(numbers.test("abc123")); // → true let onlyNumbers = /^\d+$/; console.log(onlyNumbers.test("123")); // → true console.log(onlyNumbers.test("123abc")); // → false console.log(onlyNumbers.test("abc123")); // → false
\b
:匹配單詞邊界javascriptlet wordBoundary = /\bcat\b/; console.log(wordBoundary.test("The cat is")); // → true console.log(wordBoundary.test("category")); // → false
2. 前瞻(Lookahead)
前瞻是一種特殊的匹配條件,允許你檢查某個模式是否存在於匹配的位置,但不會將這些字元包含在匹配結果中。前瞻可以分為兩種類型:正向前瞻和負向前瞻。
2.1 正向前瞻 (?=pattern)
要求後面匹配特定模式,但不消耗這些字元。
let followedByE = /a(?=e)/; // a 正後面必須有 e
console.log(followedByE.exec("braeburn")); // → ["a"]
console.log(followedByE.exec("brain")); // → null
console.log(followedByE.exec("babe")); // → null,e 必須在 a 的下一個字元
2.2 負向前瞻 (?!pattern)
要求後面不匹配特定模式,但不消耗這些字元。
let notFollowedBySpace = /a(?! )/; // a 正後面不能有空格
console.log(notFollowedBySpace.test("a b")); // → false
console.log(notFollowedBySpace.test("ab")); // → true
console.log(notFollowedBySpace.test("ab ")); // → true,空格必須在 a 的下一個字元
創建日期範例
為了確保創建日期的格必為 MM-DD-YYYY
,可以改成如下格式:
function getDate(string) {
let [_, month, day, year] =
/^(\d{1,2})-(\d{1,2})-(\d{4})$/.exec(string) || [];
if (!month || !day || !year) return "Invalid Date";
return new Date(year, month - 1, day);
}
console.log(getDate("01-30-2003")); // → Thu Jan 30 2003 00:00:00 GMT+0100 (CET)
console.log(getDate("1-30-2003")); // → Thu Jan 30 2003 00:00:00 GMT+0100 (CET)
console.log(getDate("100-1-30000")); // → Invalid Date
組合使用範例
// 匹配以字母開頭,後面跟著數字,但不能跟著空格的字串
let complexPattern = /^[a-z](?!\s)\d+$/;
console.log(complexPattern.test("a123")); // → true
console.log(complexPattern.test("a 123")); // → false
console.log(complexPattern.test("1a123")); // → false
運作機制
1. 選擇模式
在正則表達式中,可以使用管道符號 |
來表示選擇模式,其允許匹配多個不同的選擇。
let animalCount = /\d+ (pig|cow|chicken)s?/;
console.log(animalCount.test("15 pigs"));
// → true
console.log(animalCount.test("15 pugs"));
// → false
這個例子中的模式可以匹配:
- 數字後面接著 "pig"、"cow" 或 "chicken"
- 最後可以選擇性地加上 "s" (用
s?
表示)
2. 匹配機制
當正則表達式引擎開始匹配時,它會:
- 從字串的開頭開始嘗試匹配
- 如果當前位置無法匹配,則移動到下一個字元繼續嘗試
- 直到找到一個匹配或到達字串結尾
let pattern = /abc/;
console.log(pattern.test("abcde")); // → true
console.log(pattern.test("abdce")); // → false
2.1 回溯機制
當正則表達式包含多個可能的匹配路徑時,引擎會使用回溯:
- 記錄當前的匹配位置
- 嘗試一個可能的匹配路徑
- 如果失敗,則返回之前的位置嘗試其他路徑
let number = /^(\d+|0x[0-9a-f]+)$/i;
console.log(number.test("123")); // → true
console.log(number.test("0xFF")); // → true
3. 效能考量
某些正則表達式模式可能導致過多的回溯,影響效能:
// 不好的寫法 - 可能導致過度回溯
let badPattern = /([01]+)+b/;
// 更好的寫法
let goodPattern = /[01]+b/;
效能建議:
- 避免過度使用嵌套的重複運算子 (
+
,*
) - 優先使用更具體的模式而非過於寬鬆的模式
- 當只需要簡單匹配時,考慮使用字串方法替代
實際應用:解析 INI 文件
1. 什麼是 INI 文件
INI 檔案是一種常見的設定檔格式,主要用來儲存程式的設定資訊。
1.1 檔案格式規則
- 基本結構
- 可以有一般設定(不屬於任何區段)
- 可以有多個區段(section)
- 每個區段都有自己的設定項目
- 語法規則
- 空行會被忽略
- 分號(
;
)開頭的是註解行 - 區段標題格式:
[區段名稱]
- 設定項格式:
名稱=值
searchengine=https://duckduckgo.com/?q=$1 ; 一般設定
spitefulness=9.7
; 這是註解
[larry] ; 區段開始
fullname=Larry Doe
type=kindergarten bully
[davaeorn] ; 另一個區段
fullname=Davaeorn
type=evil wizard
2. 解析器實作重點
2.1 分割行
string.split(/\r?\n/)
- 用正則表達式
/\r?\n/
來處理不同作業系統的換行符 \r?
表示可能有或沒有回車符(Windows 用\r\n
,Unix 用\n
)
2.2 核心邏輯
function parseINI(string) {
let result = {}; // 儲存所有設定
let section = result; // 目前正在處理的區段
for (let line of string.split(/\r?\n/)) {
// 處理每一行…
}
return result;
}
2.3 三種匹配模式
- 一般設定:如
name=value
javascript/^(\w+)=(.*)$/
- 區段標題:如
[section]
javascript/^\[(.*)\]$/
- 註解或空行
javascript
/^\s*(;|$)/
3. 解析結果範例
// 輸入:
name=Vasilis
[address]
city=Tessaloniki
// 輸出:
{
name: "Vasilis",
address: {
city: "Tessaloniki"
}
}
4. 完整程式碼
function parseINI(string) {
let result = {};
let section = result;
for (let line of string.split(/\r?\n/)) {
let match;
if (match = line.match(/^(\w+)=(.*)$/)) {
section[match[1]] = match[2];
} else if (match = line.match(/^\[(.*)\]$/)) {
section = result[match[1]] = {};
} else if (!/^\s*(;|$)/.test(line)) {
throw new Error("Line '" + line + "' is not valid.");
}
};
return result;
}
console.log(parseINI(`
name=Vasilis
[address]
city=Tessaloniki`));
// → {name: "Vasilis", address: {city: "Tessaloniki"}}
總結
本文透過深入淺出的方式,探討了 JavaScript 中的正則表達式(Regular Expression)。從最基本的正則表達式概念,到進階的匹配機制和實際應用,逐步展示了如何有效地使用正則表達式進行文字處理。同時也說明了不同匹配方法的特性、邊界條件處理,以及效能優化等重要概念。
關鍵要點
- 正則表達式是處理文字模式的強大工具
- 字元集合和重複模式是構建複雜匹配的基礎
- 分組機制能夠提供更精確的匹配控制
- 貪婪與非貪婪匹配影響匹配結果的範圍
- 邊界和前瞻提供了精確的位置控制
- 選擇合適的匹配方法對效能至關重要
實踐建議
- 優先使用字面量方式創建正則表達式
- 善用測試方法確認匹配結果
- 注意貪婪匹配可能帶來的問題
- 合理使用分組提高匹配精確度
- 考慮效能因素選擇適當的匹配策略
以上內容是基於 9. Regular Expressions - Eloquent JavaScript 4th edition (2024) 所整理的精簡筆記。