PHP 正規表達式 (Regular Expression)
正規表達式是一種用於描述字串模式的語法,可以用來搜尋、比對和取代文字。PHP 使用 PCRE (Perl Compatible Regular Expressions) 函數庫,所有相關函數都以 preg_ 開頭。
preg_match()
檢查是否符合模式:
<?php
$pattern = '/hello/';
$text = 'hello world';
if (preg_match($pattern, $text)) {
echo "找到了";
}
// 取得匹配結果
if (preg_match($pattern, $text, $matches)) {
print_r($matches);
// Array ( [0] => hello )
}
?>
preg_match_all()
找出所有匹配:
<?php
$pattern = '/\d+/';
$text = 'I have 3 apples and 5 oranges';
preg_match_all($pattern, $text, $matches);
print_r($matches[0]);
// Array ( [0] => 3, [1] => 5 )
?>
preg_replace()
取代匹配的內容:
<?php
$pattern = '/\s+/';
$replacement = ' ';
$text = 'hello world';
$result = preg_replace($pattern, $replacement, $text);
echo $result; // hello world
// 多個模式
$patterns = ['/\d+/', '/[a-z]+/'];
$replacements = ['#', '*'];
$result = preg_replace($patterns, $replacements, 'abc123');
?>
preg_split()
使用正規表達式分割字串:
<?php
$pattern = '/[\s,]+/';
$text = 'apple, banana cherry';
$result = preg_split($pattern, $text);
print_r($result);
// Array ( [0] => apple, [1] => banana, [2] => cherry )
?>
常用模式修飾符
修飾符加在正規表達式的結尾斜線之後,用於改變匹配行為:
<?php
// i - 不區分大小寫
preg_match('/hello/i', 'HELLO'); // true
// m - 多行模式
preg_match('/^hello/m', "world\nhello"); // true
// s - 讓 . 匹配換行
preg_match('/a.b/s', "a\nb"); // true
// u - UTF-8 模式
preg_match('/中文/u', '這是中文'); // true
?>
捕獲群組
使用括號 () 可以將部分模式組成一個「群組」,匹配後可以分別取出各群組的內容:
<?php
$pattern = '/(\d{4})-(\d{2})-(\d{2})/';
$text = '日期是 2024-12-09';
if (preg_match($pattern, $text, $matches)) {
echo "完整匹配:" . $matches[0]; // 2024-12-09
echo "年:" . $matches[1]; // 2024
echo "月:" . $matches[2]; // 12
echo "日:" . $matches[3]; // 09
}
// 命名群組
$pattern = '/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/';
if (preg_match($pattern, $text, $matches)) {
echo "年:" . $matches['year'];
echo "月:" . $matches['month'];
echo "日:" . $matches['day'];
}
?>
常用正規表達式
<?php
// Email
$email = '/^[\w\.-]+@[\w\.-]+\.[a-z]{2,}$/i';
// 手機號碼(台灣)
$phone = '/^09\d{8}$/';
// URL
$url = '/^https?:\/\/[\w\.-]+\.[a-z]{2,}/i';
// IP 地址
$ip = '/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/';
// 中文字元
$chinese = '/[\x{4e00}-\x{9fa5}]+/u';
?>
回調取代
preg_replace_callback() 讓你可以用函數來處理每個匹配的結果,適合需要動態計算取代內容的情況:
<?php
$text = 'hello world';
$result = preg_replace_callback(
'/\b\w+\b/',
function($matches) {
return ucfirst($matches[0]);
},
$text
);
echo $result; // Hello World
?>
驗證範例
<?php
class Validator {
public static function email(string $value): bool {
return (bool) preg_match(
'/^[\w\.-]+@[\w\.-]+\.[a-z]{2,}$/i',
$value
);
}
public static function phone(string $value): bool {
return (bool) preg_match('/^09\d{8}$/', $value);
}
public static function password(string $value): bool {
// 至少 8 字元,包含大小寫和數字
return (bool) preg_match(
'/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{8,}$/',
$value
);
}
}
// 使用
if (Validator::email('test@example.com')) {
echo "Email 有效";
}
?>
擷取資料
<?php
// 擷取 HTML 標籤內容
$html = '<h1>標題</h1><p>內容</p>';
preg_match_all('/<(\w+)>([^<]+)<\/\1>/', $html, $matches);
// $matches[1] = ['h1', 'p']
// $matches[2] = ['標題', '內容']
// 擷取連結
$html = '<a href="https://example.com">Link</a>';
preg_match('/href="([^"]+)"/', $html, $matches);
echo $matches[1]; // https://example.com
?>
效能注意事項
<?php
// 如果只是簡單搜尋,使用字串函數更快
strpos($text, 'hello'); // 比 preg_match('/hello/', $text) 快
// 避免災難性回溯
// ❌ 危險的模式
// preg_match('/^(a+)+$/', $text);
// ✅ 使用原子群組或獨佔量詞
// preg_match('/^(?>a+)+$/', $text);
?>