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

PHP 截取 HTML 字串並自動補全標籤的實作技巧

     編輯:PHP 相關知識

PHP 截取 HTML 字串並自動補全標籤的實作技巧

在 PHP 開發中,常需要截取包含 HTML 標籤的字串來生成摘要或預覽。若直接使用 strip_tags 移除標籤,雖能獲得純文字,但會失去排版結構,閱讀體驗大打折扣。更棘手的是,若截斷點落在未閉合的標籤中間,會導致整頁版面錯亂。本文介紹兩種實用的解決方案,能在指定長度截取 HTML 內容的同時,自動補全未閉合的標籤,確保輸出的 HTML 結構完整。

方案一:基於堆疊的標籤追蹤法

此方法將 HTML 字串拆解為標籤與文字節點,利用堆疊追蹤開啟的標籤,並在截斷後依序補全閉合標籤。核心邏輯包含三個輔助函數:

  • subHtml($html, $length):主函數,負責解析 HTML、計算文字長度、管理標籤堆疊
  • mstrlen($str, $charset):精確計算 UTF-8 字串長度(含中英文)
  • msubstr($string, $start, $length, $dot, $charset):多位元組安全的子字串截取
function subHtml($html, $length) {
    $result = '';
    $tagStack = [];
    $len = 0;
    
    // 以正則拆分標籤與文字,保留分隔符
    $contents = preg_split("~(<[^>]+>)~si", $html, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
    
    foreach ($contents as $tag) {
        if (trim($tag) === '') continue;
        
        // 自閉合標籤 
、 等直接輸出 if (preg_match("~<([a-z0-9]+)[^/>]*/>~si", $tag)) { $result .= $tag; } // 閉合標籤
等,匹配堆疊頂端則彈出 elseif (preg_match("~</([a-z0-9]+)[^/>]*>~si", $tag, $match)) { if (!empty($tagStack) && end($tagStack) === $match[1]) { array_pop($tagStack); $result .= $tag; } } // 開啟標籤
等,推入堆疊 elseif (preg_match("~<([a-z0-9]+)[^/>]*>~si", $tag, $match)) { array_push($tagStack, $match[1]); $result .= $tag; } // 註釋標籤直接輸出 elseif (preg_match("~<!--.*-->~si", $tag)) { $result .= $tag; } // 純文字節點,依長度截取 else { if ($len + mstrlen($tag) < $length) { $result .= $tag; $len += mstrlen($tag); } else { $str = msubstr($tag, 0, $length - $len + 1); $result .= $str; break; } } } // 補全剩餘未閉合標籤(反序彈出) while (!empty($tagStack)) { $result .= '</' . array_pop($tagStack) . '>'; } return $result; }

方案二:字元逐一掃描法

另一種思路是逐字元掃描,遇到 < 進入標籤解析模式,記錄開啟與閉合標籤名稱,最後根據未配對的開啟標籤生成閉合標籤。此法對巢狀結構處理較直觀,但需注意 HTML 實體編碼與屬性值中夾帶的 > 字元。

function phpos_chsubstr_ahtml($str, $num, $more = false) {
    $leng = strlen($str);
    if ($num >= $leng) return $str;
    
    $word = 0; $i = 0;
    $stag = []; $etag = [];
    $sp = 0; $ep = 0;
    
    while ($word != $num) {
        if (ord($str[$i]) > 128) { // 中文字元(UTF-8 3 位元組)
            $i += 3; $word++;
        }
        elseif ($str[$i] === '<') { // 進入標籤解析
            // 解析標籤名稱,區分開啟/閉合標籤,存入 $stag/$etag
            // ...(略,完整邏輯見原始碼)
            $i++;
            continue;
        }
        else { $word++; $i++; }
    }
    
    // 配對消除已閉合標籤,保留未閉合的開啟標籤
    foreach ($etag as $val) {
        $key = array_search($val, $stag);
        if ($key !== false) unset($stag[$key]);
    }
    // 過濾無需閉合標籤
    foreach ($stag as $key => $val) {
        if (in_array($val, ['br', 'img'])) unset($stag[$key]);
    }
    
    // 反序生成閉合標籤
    $stag = array_reverse($stag);
    $ends = '</' . implode('></', $stag) . '>';
    $re = substr($str, 0, $i) . $ends;
    if ($more) $re .= '...';
    return $re;
}

方案三:利用 mb_ereg 正則配合堆疊(適用於文章摘要)

此法針對常見區塊標籤(div、p、table 等)進行正則匹配,配合堆疊管理巢狀關係,適合 CMS 系統的文章摘要生成:

function text_zhaiyao($text, $length) {
    mb_regex_encoding("UTF-8");
    if (mb_strlen($text) <= $length) return $text;
    
    $Foremost = mb_substr($text, 0, $length);
    $re = "<(/?) (P|DIV|H[1-6]|TABLE|TR|TD|TH|A|UL|OL|LI|SPAN|IMG|BR|HR)[^>]*(>?)";
    $Single = "/BR|HR|IMG|INPUT/i"; // 無需閉合標籤
    
    $Stack = []; $posStack = [];
    mb_ereg_search_init($Foremost, $re, 'i');
    
    while ($pos = mb_ereg_search_pos()) {
        $match = mb_ereg_search_getregs();
        // $match[1]:是否為閉合標籤 (/)
        // $match[2]:標籤名稱
        // $match[3]:右括號 (>)
        if ($match[1] === "") { // 開啟標籤
            $Elem = $match[2];
            if (mb_eregi($Single, $Elem) && $match[3] !== "") continue;
            array_push($Stack, $Elem);
            array_push($posStack, $pos[0]);
        } else { // 閉合標籤
            // 匹配堆疊頂端則彈出
        }
    }
    // 依堆疊剩餘標籤補全閉合標籤...
}

實務建議與注意事項

  1. 編碼一致性:確保 PHP 檔案、資料庫連線、HTML 頁面均為 UTF-8,避免多位元組字元截斷產生亂碼。
  2. 效能考量:對於超長 HTML 內容,正則拆分與逐字元掃描均有效能開銷。可考慮在資料寫入時預先生成純文字摘要,或使用 DOMDocument 解析樹狀結構(雖較慢但更健壯)。
  3. 特殊標籤處理:<script>、<style> 內容通常不應出現在摘要中,建議預先移除。
  4. XSS 風險:若 HTML 來源不可信,截取前應先進行淨化(如使用 HTMLPurifier),防止殘留惡意腳本。

掌握這些技巧,能讓你在開發文章列表、搜尋結果預覽、RSS 輸出等場景時,既保留豐富排版,又確保頁面結構穩定。

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