兩份 TXT 先對行數
起因很樸素:我手上有兩份 TXT,一份 555 段、一份 819 段。想看的不是內容,而是逐行對比頁在語料級輸入下給出的賬——公共行怎麼數、增刪按什麼先後、統計那行寫了幾個數。兩份文件只當字節與行的語料,正文不引句子。
語料的字節與段數
兩份都是站內已發布資源:《聲律啟蒙》26353 字節、9185 碼元、9183 碼點,555 段、非空 403、空行 152;《笠翁對韻》28903 字節、10211 碼元、10209 碼點,819 段、非空 671、空行 148;都無 CRLF、也無 BOM。碼元比碼點多 2,來自兩份各有 2 個增平面碼點。被測頁就是文本對比,模板 118 行、4487 字節、腳本 3 個函數。
整檔餵進去的三筆賬
兩份全文直接調 lineDiff:added 665、removed 401、equal 154、結果 1220 條,dp 表 556 × 820 = 455920 個單元格,規模代價舊文算過,這檔數本篇只登記。恆等式:555 減 154 得 401、819 減 154 得 665、555 加 819 減 154 得 1220;方向對調則增刪兩數互換,equal 與單元格數不變。
| 輸入檔位 | A 段 | B 段 | removed | added | equal | 結果條數 |
|---|---|---|---|---|---|---|
| 全文切段 | 555 | 819 | 401 | 665 | 154 | 1220 |
| 先去掉空行 | 403 | 671 | 397 | 665 | 6 | 1068 |
| 兩檔各自自比 | 555 與 819 | 同左 | 0 | 0 | 555 與 819 | 同左 |
兩檔 added 同為 665,差額只在 removed:A 側 152 個空行被吸收 148 個,剩 4 個進刪除賬,所以是 401;去空行那檔的 dp 表為 404 × 672 = 271488。耗時只說腳本層:本地腳本連調 5 輪得 113.9、139.5、120.5、122.7、117.6 ms,中位數 120.5 ms——它是單次函數調用的耗時,不是頁面回應。
守衛只在兩側都空時命中
doDiff() 裡兩側 join 後 trim、都成空串才 toast「請輸入要比較的文本」並 return:判據是合起來全空白,不是逐側。四條實測:
- 兩側都空、或 A 側只有 3 個半角空格:命中後什麼都不改,統計與顯示停在上一輪。
- 一側空、另一側 403 條非空語料行:照跑,統計給「新增 403 行,刪除 1 行」。
- 把那 403 行挪到 A 側:鏡像的「新增 1 行,刪除 403 行」。
- 一側空、另一側換成全 555 段:給「新增 554 行,刪除 0 行」。
關鍵在最後一條:空串切出 1 段,對面沒空行時它落成刪除 1 行;對面有 152 條空行時被 LCS 吸收、刪除歸 0,新增變成 554。
圖 1:守衛的兩檔與空行改賬。
同分取捨定出刪前增後
回溯條件寫成 dp[i][j-1] >= dp[i][j]:同分先進 added 分支、j 減 1;回溯從表尾往回走,末尾又有 reverse(),於是輸出流固定刪前增後:
- a、b 對 b、a:得 removed a、equal b、added a;走完
doDiff後 A 側是「- a、b、佔位」、B 側「佔位、b、+ a」。 - old 對 new:removed 與 added 相鄰一對;a、b 對 a、c:equal、removed、added。
- 1、2、3、4 對 3、4、1、2:removed 1、2,equal 3、4,added 1、2——公共段錨在 3 與 4。
另開上下文只把 >= 換成 >:同批樣本 4 組順序翻轉、塊互換把錨挪到 1 與 2,另有 1 組逐行一模一樣——只作對照紀錄。
移動一行:取 10 行、把第 2 行插到第 9 位,自比 0 增 0 刪,挪後 added 1、removed 1、equal 9、結果 11 條——代碼裡沒有移動檢測。
統計那行只有兩個數
頁面那行串逐字是 新增 ${added} 行,删除 ${removed} 行,抽出數字恰好 2 個,整檔成品「新增 665 行,刪除 401 行」;公共行數與 dp 終值的 LCS 長度都不進串。上方文案「統計概覽:顯示新增行數、刪除行數、修改行數」這半句與代碼衝突:按修改計數的分支要行類型不屬於 equal、removed、added 才進,而三處 push 的字面量恰是這三種,1220 條那檔的類型集合仍是三個;改一行裡幾個字元也是整行落進兩側——詞級那層本頁沒有(舊文登記過)。
結果行進 DOM 的方式與殘留
渲染逐條 createElement 配 textContent:每側 1220 個 div、兩側共 2440 個一次全進;容器 max-height 400px 加 overflow auto,對照實驗 scrollHeight 29328px 對 400px。本頁無轉義函數也無複製按鈕,模板裡 <button 命中 2 個;含尖括號、& 與引號的行原樣讀回,對側佔位是單個 U+00A0;「清空」只把顯示設回隱藏,兩側各殘留 2 行。
CRLF 分兩層,舊文那條只登記一句複核:字串層帶 \r 的行對不帶的是 1 刪 1 增;走 textarea 則回車歸一成換行,貼上去得 0 增 0 刪。尾隨 LF 另有一筆:切分各多出 1 個空段,對去尾 LF 版實測 equal 554、刪除 1、新增 0。
圖 2:整檔對跑的三筆賬。
公共行的三本賬與它們落在檔尾
公共行的口徑有三本賬:多重集下含空行 154 條,空行按少的一檔貢獻 148、非空只剩 6;按種類去重得 6 種、再去空串剩 5 種,28 碼元的分隔線兩側各出 2 次;同位置的口徑只有 25 行。這 6 條不在文件開頭而在尾部說明塊——前者第 544 至 553 行、後者第 808 至 817 行——字串本身不搬進正文。整檔 LCS 恰是 154、去空行檔 LCS 恰是 6;與多重集交集相等只因兩側先後一致,是巧合、不是恆等式。
常見問題
Q:一側什麼都不填,會被當成錯誤攔下來嗎?
不會,守衛要兩側合起來全空白才命中;一側空、另一側 403 條非空行照跑,給「新增 403 行,刪除 1 行」。
Q:只挪動了一行,為什麼刪與增各漲 1?
結果類型裡沒有移動這一項,lineDiff 只產出 equal、added、removed 三種,挪一行就記 added 1、removed 1。
Q:公共行 154 條,為什麼統計裡看不見?
統計串只有「新增 665 行,刪除 401 行」兩個數,公共行數與 LCS 長度都不進串。
資源說明
兩處資源連結只交代語料出處:兩部書在這裡只按字節與行計數,正文不引句子、不作內容層面的解釋,尾部那 6 條公共字串只作行計數登記。頁面沒有檔案輸入通道,整檔只能靠貼上。