三個數算的是三種帳
貼一段 HTML 跑完,統計那行只給三個數:移除標籤 19、原始字元 227、結果字元 79。可 19 和 148 不是同一種量。把這頁範本(150 行 / 6391 位元組)裡唯一的處理函式逐字抽出來在 Node 裡驅動,跑同一串 13 行 / 227 字元的樣例:19 是 6 處 .match() 命中數之和(第 92 行起),227 與 79 是第 146、147 行的兩個 .length,相減的 148 才是真正消失的字元數——每報 1 個標籤,就有 7.79 個字元走掉。
模式(去除所有 / 去除指定)× 保留標籤內內容 × 去註釋 × 壓空格,4 個開關展開成 16 組。在 HTML 標籤去除 上把這 16 格逐格撥一遍,同一串 227 字元的結果全在下表。
16 組矩陣:只有 1 個開關動計數
| 組 | 模式 | 內容 | 註釋 | 空格 | 移除標籤 | 實刪字元 |
|---|---|---|---|---|---|---|
| 1 | all | 開 | 開 | 開 | 19 | 148 |
| 2 | all | 開 | 開 | 關 | 19 | 140 |
| 3 | all | 開 | 關 | 開 | 19 | 136 |
| 4 | all | 開 | 關 | 關 | 19 | 129 |
| 5 | all | 關 | 開 | 開 | 14 | 181 |
| 6 | all | 關 | 開 | 關 | 14 | 172 |
| 7 | all | 關 | 關 | 開 | 14 | 169 |
| 8 | all | 關 | 關 | 關 | 14 | 161 |
| 9 | 指定 | 開 | 開 | 開 | 10 | 86 |
| 10 | 指定 | 開 | 開 | 關 | 10 | 80 |
| 11 | 指定 | 開 | 關 | 開 | 10 | 74 |
| 12 | 指定 | 開 | 關 | 關 | 10 | 69 |
| 13 | 指定 | 關 | 開 | 開 | 5 | 133 |
| 14 | 指定 | 關 | 開 | 關 | 5 | 126 |
| 15 | 指定 | 關 | 關 | 開 | 5 | 121 |
| 16 | 指定 | 關 | 關 | 關 | 5 | 115 |
第 9 到 16 組填的是同一串 4 個標籤名。四個開關裡只有保留內容會動計數:移除標籤在四個區段恆為 19、14、10、5,註釋與空格一個都不影響它,結果字元只隨這兩個開關變。保留內容一撥,報數從 19 掉到 14、實刪反而從 148 漲到 181。
圖 1:四個區段各取註釋與空格都開的那一格。
保留內容一關,被刪的標籤不上數
all 模式關掉這一檔會換一條路徑:第 96 行那 6 個 blockTags 先被第 98 行的整塊正則刪掉,第 102 行才開始計數,所以塊的開閉標籤一個都不進「移除標籤」。樣例上 19 掉到 14,差的 5 個正是 <style>、</style>、<script>、</script> 和字串字面量裡的 <div>。
54 個字元的一串:<style>.a{}</style><script>var a="<div>";</script>text。保留內容開著是移除 5、輸出 17 字元;關掉只剩 4 字元的 text,移除標籤卻寫 0。
指定標籤做的是前綴匹配
標籤名填進去後,第 115 行拼出的正則 '<'+tag+'[^>]*>' 前面沒有邊界。填 p,作用在 40 個字元的 <p>段落</p><pre> 程式碼</pre><param name="x"> 上:移除標籤 4、輸出 12 字元,<pre> 與 <param name="x"> 一起被刪,closeRe 卻要求 </p\s*>,閉標籤留成殘渣——刪掉的 28 個字元只報了 4。填 i,69 個字元刪到 10,img、iframe、input 三個開標籤全沒。
分隔符少一種,整串一字未改
欄位標籤寫的是逗號分隔,清單兩處示範卻是頓號。第 109 行 split(/[,,\s]+/) 只認半形逗號、全形逗號和空白,頓號不在名單裡:照文案輸入 script、style 只切出 1 個 token、淨化成 scriptstyle,移除 0,84 個字元一字未改;同一串寫成 script,style,div 就是移除 6、刪掉 43 個。空值更安靜:28 字元的一串移除 0、輸出 16 字元,而同一串在 all 模式是移除 2、輸出 5 字元——移除 0 不等於輸出等於輸入。
「所有」這個詞撐不住
通用正則要求 < 後緊跟 ASCII 字母且必須有閉合的 >,預設組合有 7 類標記原樣留下:<!DOCTYPE html> 的 15 個字元、<?xml version="1.0"?> 的 21、<3.5 的 4、<> 的 2、</> 的 3、未閉合 <div 的 4、< b>x</ b> 的 10。實體形態也不還原:<div> 的 11 個字元 0 匹配、一路留到輸出, 與 U+00A0 同樣不在處理範圍;比較運算子也會被當成標籤,<b && c> 這 8 個字元就是被吞掉的那個。
註釋與空格這兩檔
註釋是第 85 行的非貪婪匹配,遇到第一個結束符就收:未閉合的 17 字元原樣保留,巢狀的只吃掉 19、留 6 個字元殘渣。
空格是第 136 行裡的 3 個 .replace(),壓成 1 個空格而不是刪除:中文段 9 個字元變 7;全形空格 U+3000 的 9 變 8,只刪行首那 1 個;U+00A0 的 4 個不變、U+200B 的 3 個不變;單獨換行不合併,3 連換行才從 5 變 3;CRLF 從 6 變 4。程式碼縮排最受傷:<pre> 段剝掉標籤是 25 字元,開了壓縮剩 19,首行縮排被直接刪光。
一整頁網頁跑下來
換一頁 19 行 / 569 字元的真實結構:預設組合報 32 個標籤、輸出 277 字元,第一行仍是 <!DOCTYPE html>,其中漢字只有 52 個、程式碼符號 53 個。只關保留內容,報數變 26、輸出 88 字元,兩版差的 189 個字元正是 CSS / JS / JSON-LD 原始碼的體量。
沒有批次入口,也沒有第二遍的驚喜
「批量」在這頁不存在:第 70 行只讀 1 個輸入框的值、第 44 行只有 1 個輸出區,0 處檔案入口、0 處 FileReader、0 處遍歷迴圈,跑第二段只能手工全選替換;6 個呼叫點無節流,5000 字元每敲一個字都重跑 12 次 .replace()。把輸出灌回去跑第二遍,11 組裡 10 組逐位元組不變,只有 <<b>hi> 繼續被改:第一遍輸出 4 字元的 <hi>,第二遍輸出 0 字元,而第二遍的移除標籤普遍顯示 0——它是本次快照、不是進度。
22 條文案對照
把 config/tools.php 第 1144–1152 行的 22 條文案逐條對著實測判一遍:[一致] 7 條、[衝突] 15 條,衝突集中在 「提取纯文本」、「仅移除指定标签」、「可选去除连续空格和换行」 這三句。站內那篇講去除 HTML 標籤的舊文是經驗向敘述,這篇不重複它,只把開關矩陣與計數補齊。
常見問題
Q:報 19 個標籤,就是刪掉 19 個標籤嗎?
不是。同一串 227 字元,保留內容開著報 19、關掉報 14,實刪字元反而從 148 漲到 181:整塊刪除先跑、計數後跑,比值從 7.79 跳到 12.93。
Q:只填 p,為什麼連 <pre> 一起沒了?
第 115 行的正則沒有前綴邊界,p 會命中 pre 與 param;閉標籤那邊又要求一字不差,於是開標籤被刪、閉標籤留成殘渣。
Q:結果裡還留著實體形態怎麼辦?
這頁 0 處實體解碼、0 處 DOM 解析,全是字串替換:< 這種形態會一路留到輸出。