正則表達式: Character | Quantifier | Grouping | Anchors | Character Range | Examples
範例: Grouping | Lookbehind
Intro
正則表達式
1. 字元匹配基礎(Character Matching Basics)
語法 | 分類名稱 | 說明 |
---|---|---|
. |
通用字元匹配符 (Wildcard) | 匹配任意一個字元(通常不含換行符) |
[abc] |
字元集合 (Character Set) | 匹配集合內任一字元 |
[^0-9] |
否定字元集合 (Negated Character Set) | 匹配不在集合內的字元 |
\d |
數字字元類 (Digit Character Class) | 匹配任意數字 (0-9) |
\w |
單字字元類 (Word Character Class) | 匹配字母、數字、底線 |
\s |
空白字元類 (Whitespace Character Class) | 匹配空格、tab、換行等空白字元 |
\b |
單字邊界 (Word Boundary Anchor) | 單字與非單字字元的邊界 |
^ |
字串開頭錨點 (Start Anchor) | 匹配字串開始位置 |
$ |
字串結尾錨點 (End Anchor) | 匹配字串結尾位置 |
- 中文等 Unicode 處理依照程式語言,要注意使用 Unicode 標誌(如 JavaScript 的
/u
)或相應設定,才能正確匹配。
範例參考:Lookbehind 任意中文範例.
預設不匹配換行符,需要加s
模式或用[\s\S]
2. 量詞(Quantifiers)
語法 | 說明 | 範例 |
---|---|---|
* |
0 次或多次 | a* 可匹配空字串、a、aaa |
+ |
1 次或多次 | a+ 可匹配 a、aaa |
? |
0 次或 1 次 | a? 可匹配有或沒有 a |
{n} |
恰好 n 次 | a{3} 可匹配 aaa |
{n,} |
至少 n 次 | a{2,} 可匹配 aa、aaa... |
{n,m} |
至少 n 次,至多 m 次 | a{2,4} 可匹配 aa、aaa、aaaa |
3. 群組與擷取(Grouping and Capturing)
在使用純字元匹配的正則表達式時,命中的結果稱為 match (整體匹配)。當正則中使用括號 (pattern) 時,括號內匹配的部分稱為 group (群組),可以用來擷取子串或做回溯引用。
match & group 匹配的程式語言實作可以參考本篇 正則群組擷取範例
語法 | 說明 | 範例 |
---|---|---|
( ... ) |
捕捉群組 (Capturing Group) | (ab)+ 捕捉並重複 ab |
(?: ... ) |
非捕捉群組 (Non-capturing Group) | (?:ab)+ 只分組但不捕捉 |
\1 , \2 |
回溯引用 (Backreference) | <(\w+)>.*<\/\1> HTML 標籤匹配 |
(?P<name> ... ) |
命名捕捉群組 (Named Capturing Group) | Python 中 (?P<first>\w+) |
4. 位置與條件判斷(Anchors and Assertions)
語法 | 說明 | 範例 |
---|---|---|
^ |
字串開頭錨點 (Start Anchor) | ^Hello |
$ |
字串結尾錨點 (End Anchor) | world$ |
\b |
單字邊界 (Word Boundary) | \bword\b |
(?= ... ) |
正向先行斷言(Lookahead) | \d(?=元) 配在「元」前的數字 |
(?! ... ) |
負向先行斷言(Negative Lookahead) | \d(?!元) 不配「元」前的數字 |
(?<= ... ) |
正向後顧斷言(Lookbehind) | (?<=\$)\d+ 配在 $ 後的數字 |
(?<! ... ) |
負向後顧斷言(Negative Lookbehind) | (?<!\$)\d+ 配非 $ 開頭的數字 |
Lookahead 與 Lookbehind 雖然使用括號 (),和一般群組(grouping)語法相同,但它們屬於特殊的「零寬度斷言」(zero-width assertions)。這類斷言不會消耗字元,也不會將括號內的內容當作捕捉群組(capturing group),因此不會出現在 match 的群組結果中,而是僅用來判斷匹配位置是否符合特定條件。
在實務應用中,若需抓取符合某條件之後的字串,通常可以透過一般的群組語法 (pattern) 來實現,並從群組(如 group 1)中擷取目標結果;而使用 Lookahead 或 Lookbehind 則可以讓最終匹配結果(即整體 match)直接是目標字串本身,更簡潔也更具彈性。
範例比較:Grouping vs Lookbehind
Lookbehind 效能較差,且並非所有語言或正則引擎皆支援,例如 Go、Rust 等採用簡化實作的正則引擎可能無法使用。
5. 字元範圍與 Unicode 支援(Character Ranges & Unicode)
語法 | 說明 | 範例 |
---|---|---|
[a-z] |
小寫字母範圍 | 匹配 a~z |
[A-Z] |
大寫字母範圍 | 匹配 A~Z |
[0-9] |
數字範圍 | 匹配 0~9 |
[\u4e00-\u9fff] |
Unicode 中文範圍 | 匹配漢字範圍 |
\p{L} , \p{N} |
Unicode 字母與數字類別(需語言支援) | 匹配所有字母或數字 |
6. 實務應用範例(Practical Examples)
需求 | Regex 範例 | 說明 |
---|---|---|
抓 Email 地址 | \b[\w.-]+@[\w.-]+\.\w+\b |
基本 Email 格式匹配 |
抓網址 | https?://[^\s]+ |
匹配 http 或 https 開頭網址 |
抓三個字的中間字 | ^.(.).$ |
抓三字詞中間那字 |
抓最後 5 個字 | .{5}$ |
抓字串末尾 5 個字 |
抓字串中特定字後的數字 | (?<=價格:)\d+ |
抓「價格:」後面的數字 |
範例
正則群組擷取範例
JavaScript
const str = '王小明';
const regex = /^.(.).$/;
const result = str.match(regex);
console.log(result[0]); // 整體 match,輸出:王小明
console.log(result[1]); // group 1,輸出:小
Python
import re
text = "王小明"
pattern = r"^.(.).$"
match = re.match(pattern, text)
if match:
print(match.group(0)) # 整體 match,輸出:王小明
print(match.group(1)) # group 1,輸出:小
Java
import java.util.regex.*;
public class RegexGroupExample {
public static void main(String[] args) {
String text = "王小明";
Pattern pattern = Pattern.compile("^.(.).$");
Matcher matcher = pattern.matcher(text);
if (matcher.find()) {
System.out.println(matcher.group(0)); // 整體 match,輸出:王小明
System.out.println(matcher.group(1)); // group 1,輸出:小
}
}
}
PHP
<?php
$text = "王小明";
$pattern = "/^.(.).$/u";
if (preg_match($pattern, $text, $matches)) {
echo $matches[0] . "\n"; // 整體 match,輸出:王小明
echo $matches[1] . "\n"; // group 1,輸出:小
}
?>
Lookbehind 斷言範例
Python
import re
text = "王小明"
pattern = r"(?<=^.)[^\n]" # 往前看前一字元為任意一字元(即第二個字)
match = re.search(pattern, text)
if match:
print(match.group()) # 輸出:小(整體 match,即第二個字)
JavaScript (ES2018+ 支援)
const text = "王小明";
const regex = /(?<=^.)[^\n]/u;
const match = text.match(regex);
if (match) {
console.log(match[0]); // 輸出:小
}
.NET (C#)
using System;
using System.Text.RegularExpressions;
class Program {
static void Main() {
string text = "王小明";
Regex regex = new Regex(@"(?<=^.)[^\n]", RegexOptions.None);
Match match = regex.Match(text);
if (match.Success) {
Console.WriteLine(match.Value); // 輸出:小
}
}
}
Java (Java 9+ 支援)
import java.util.regex.*;
public class LookbehindExample {
public static void main(String[] args) {
String text = "王小明";
Pattern pattern = Pattern.compile("(?<=^.)[^\n]");
Matcher matcher = pattern.matcher(text);
if (matcher.find()) {
System.out.println(matcher.group()); // 輸出:小
}
}
}
PHP (PCRE 支援)
<?php
$text = "王小明";
$pattern = "/(?<=^.)[^\n]/u";
if (preg_match($pattern, $text, $matches)) {
echo $matches[0] . "\n"; // 輸出:小
}
?>