首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

正則表達式進階:選擇分支、分組捕獲與後向引用

     編輯:PHP 綜合知識

正則表達式的強大在於「選擇」、「分組」與「引用」三大機制的組合運用。本文深入解析分支結構 `|`、捕獲分組 `()`、非捕獲分組 `(?:)`、前瞻斷言 `(?=)`/`(?!)`、後向引用 `\1` 等核心概念,並通過實戰案例展示其應用場景。

選擇分支:縱向符 `|`

匹配多個備選模式之一,優先級最低,左右嘗試匹配。

/cat|dog|bird/  # 匹配 cat 或 dog 或 bird

常見陷阱:優先級問題

/^Chapter|Section [1-9][0-9]?$/  # 錯誤!實際匹配:行首 Chapter 或 行尾 Section 12

/^(Chapter|Section) [1-9][0-9]?$/  # 正確:分組限制選擇範圍

分組與捕獲

語法名稱捕獲用途
(pattern)捕獲分組是提取子串、後向引用、替換引用 $1
(?:pattern)非捕獲分組否僅分組不捕獲,提升性能
(?<name>pattern)命名捕獲組是PHP/PCRE 支援,\k<name> 或 $matches['name'] 引用
(?=pattern)正向前瞻否斷言後方匹配,不消耗字符
(?!pattern)負向前瞻否斷言後方不匹配
(?<=pattern)正向後顧否斷言前方匹配 (固定寬度)
(?<!pattern)負向後顧否斷言前方不匹配

實戰案例

1. Windows 版本號提取與替換

<?php
$text = "Windows 3.1, Windows 95, Windows 98, Windows NT, Windows 2000";

// 僅匹配 95/98/NT/2000 後的 Windows
$pattern = '/Windows(?= 95| 98| NT| 2000)/';
preg_match_all($pattern, $text, $matches);
// $matches[0] = ['Windows', 'Windows', 'Windows', 'Windows']

// 替換為 Windows 11
$new_text = preg_replace('/Windows(?= 95| 98| NT| 2000)/', 'Windows 11', $text);
// 結果: "Windows 3.1, Windows 11, Windows 11, Windows 11, Windows 11"

2. 連續重複單詞檢測與去重

<?php
$text = "Is is the cost of of gasoline going up up?";

// \b 邊界,([a-z]+) 捕獲單詞,\1 後向引用
$pattern = '/\b([a-z]+)\s+\1\b/i';
$clean = preg_replace($pattern, '$1', $text);
// 結果: "Is the cost of gasoline going up?"

// 進階:檢測跨行重複
$pattern_m = '/\b([a-z]+)\s+\1\b/im';

3. URI 解析:協議、域名、端口、路徑

<?php
$uri = "https://api.example.com:8443/v1/users/profile";

$pattern = '/^(\w+):\/\/([^:\/]+)(?::(\d+))?(.\*)?$/';
if (preg_match($pattern, $uri, $m)) {
    // $m[0] 完整匹配
    // $m[1] 協議: https
    // $m[2] 域名: api.example.com
    // $m[3] 端口: 8443 (可選)
    // $m[4] 路徑: /v1/users/profile
    print_r($m);
}

/*
Array
(
    [0] => https://api.example.com:8443/v1/users/profile
    [1] => https
    [2] => api.example.com
    [3] => 8443
    [4] => /v1/users/profile
)
*/

4. HTML 標籤屬性提取

<?php
$html = '<img src="avatar.png" alt="用戶頭像" class="img-rounded" data-id="123" />';

// 提取所有屬性名值對
preg_match_all('/(\w+)\s*=\s*"([^"]*)"/', $html, $m, PREG_SET_ORDER);
foreach ($m as $match) {
    echo "屬性: {$match[1]} = {$match[2]}\n";
}

/*
屬性: src = avatar.png
屬性: alt = 用戶頭像
屬性: class = img-rounded
屬性: data-id = 123
*/

性能優化建議

  1. 優先使用非捕獲分組:不需要引用時用 `(?:...)`,減少內存分配
  2. 避免嵌套量詞:`(a+)+` 類模式易導致災難性回溯
  3. 錨點優化:能用 `^` `$` `\b` 盡早鎖定位置
  4. 具體字符類別:`[a-z]` 優於 `.`,減少回溯
  5. 預編譯重用:PHP 中同一模式多次使用,`$re = '/pattern/';` 避免重複編譯

PHP 專用:PREG_SET_ORDER 與 PREG_OFFSET_CAPTURE

$text = "A1 B22 C333";
$pattern = '/([A-Z])(\d+)/';

// PREG_SET_ORDER: 結果按匹配分組
preg_match_all($pattern, $text, $m, PREG_SET_ORDER);
/*
$m[0] = ['A1', 'A', '1']
$m[1] = ['B22', 'B', '22']
$m[2] = ['C333', 'C', '333']
*/

// PREG_OFFSET_CAPTURE: 返回位移
preg_match_all($pattern, $text, $m, PREG_OFFSET_CAPTURE);
/*
$m[0][0] = ['A', 0]
$m[1][0] = ['1', 1]
$m[0][1] = ['B', 3]
...
*/

總結

掌握「選擇→分組→引用」鏈路,是編寫高效、可維護正則的關鍵。記住:只捕獲你需要的,斷言不消耗字符,後向引用威力巨大但慎用嵌套。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved