雜記 › 技術筆記 › 後綴只切最後一個點:archive.tar.gz 分走 gz,photo. 和 .gitignore 擠進同一格,表格首列還會掉一個點

後綴只切最後一個點:archive.tar.gz 分走 gz,photo. 和 .gitignore 擠進同一格,表格首列還會掉一個點

後綴拆分把切點寫死在最後一個點,archive.tar.gz 只切出 gz;沒有點和點打頭的兩類輸入共用同一個空擴充名返回,photo. 這類尾點走另一條分支也落進無後綴。第一列是重建串會吃掉尾點,11 格裡有 4 格對不回輸入。

切點寫死在最後一個點:archive.tar.gz 只分到 gz

手邊攢了一堆檔名想按後綴歸堆:archive.tar.gz 到底算 gz 還是 tar.gz?photo. 算不算有後綴?開頭帶點的 .gitignore 又該進哪一列?把這幾行貼進去、點一次「拆分後綴」,結果和腦子裡那個「擴展名」並不完全對得上。差的不是按鈕,是切點被釘死在最後一個點,外加緊跟其後的一條分支把兩種輸入並到了一起。模板一共 6546 位元組 / 158 行,拆後綴這件事全住在 splitSuffix 那十來行裡。

切點走的是 filename.lastIndexOf('.'),從右往左數到的第一個點就是唯一切點。檔名後綴拆分於是把 archive.tar.gz 切成 name 為 archive.tar、ext 為 gz——多重後綴永遠只切最後那一段。11 個樣本裡點數 ≥ 2 的有 7 格,切完之後 ext 裡含點的 0 格、name 裡仍帶點的反而有 8 格:中間那些點全被留在了檔名那一側,而不是跟著進擴展名列。

archive.tar.gz 這類多重後綴,只切最後一段 樣本 切出的 name ext archive.tar.gz archive.tar gz my.file.txt my.file txt 多.重.後.綴.zip 多.重.後.綴 zip 點數 ≥ 2 的 7 格 · ext 含點 0 格 · name 仍含點 8 格

圖 1:切點釘在最後一個點,多重點全被留在檔名側。

dot <= 0 這一條,把「沒有點」和「點打頭」並成一個返回

緊跟著切點的 L66 那道 if (dot <= 0) 一次接住兩類完全不同的輸入:dot 為 -1(README 根本沒有點)和 dot 為 0(.gitignore 的點就在第 0 位)。兩者共用同一個 return { name: filename, ext: '' }——整串原樣進 name,ext 給空字串。於是 .gitignore 不被拆出擴展名、README 也一樣,兩格落進同一個「無後綴」計數。

真正容易被讀漏的是尾點那一族:photo.、.hidden.、..、name.。它們走的是 dot > 0 那條分支,點確實在串裡某處,只是 substring(dot + 1) 從點後一位切到串尾,切出來是個空字串。於是 ext 照樣為空,在 L96 的統計裡它和 README、.gitignore 一起落進 withoutExt——11 格裡這樣的有 4 格。差別只落在 name:前兩類整串進 name,尾點族的 name 被削掉了最後那個點。

逐格攤開:11 行真值表,看第一列對不回輸入的有哪幾格

下面把 11 個樣本一格一格鋪開。最後一列是「重建出來的第一列」,它等於 name 拼回 ext;拿它和修剪後的輸入逐格比,7 格對得上、4 格對不上,對不上的正是 photo. / .hidden. / .. / name. 這四個尾點樣本。

輸入檔名最後一個點nameext第一列重建串對得上?
archive.tar.gz11archive.targzarchive.tar.gz是
my.file.txt7my.filetxtmy.file.txt是
.gitignore0.gitignore—.gitignore是
.bashrc.conf7.bashrcconf.bashrc.conf是
README-1README—README是
photo.5photo—photo否
.hidden.7.hidden—.hidden否
a..b2a.ba..b是
..1.—.否
name.4name—name否
多.重.後.綴.zip7多.重.後.綴zip多.重.後.綴.zip是

重建出來的那一列,會悄悄吃掉一個尾點

第一列不是原樣回填輸入,它是按 name + (ext ? '.' + ext : '') 現拼的重建串,而輸入在進函式之前已經被剪過首尾空白。photo. 走完一圈,name 是 photo、ext 空,重建串就停在 photo,那個尾點沒了。尾點這一族通吃:三個點 ... 重建剩 ..,archive.tar.gz 後面再補一個點,回來變回 archive.tar.gz——每次恰好吞掉 1 個尾點。第二列的 name 卻仍保留中間那些點,所以兩列對不齊的根子在重建那一步,不在切分本身。

本頁所有樣本都按輸入原樣大小寫記著,後綴的大小寫歸後綴大小寫轉換那頁管。

第一列 = name +(有 ext 才補一個點)+ ext 輸入 重建出來的第一列 尾點 photo. photo 點沒了 ... .. 少 1 個點 archive.tar.gz. archive.tar.gz 少 1 個點 11 格裡:對得上 7 · 掉點 4

圖 2:重建列每次吞掉一個尾點,11 格裡 4 格對不回輸入。

統計三行和提示語,是同一本賬

右上角的統計走 L96 的 if (r.ext),判據是 ext 空不空,不是「有沒有點」。11 個樣本整批跑一遍,total 11、有後綴 5、無後綴 6,和表格的 11 行、6 個破折號格、5 個綠格、提示語那句「已拆分 11 個檔名」完全同分母,5 + 6 = 11 也咬得上。點「示例」灌進去的那 6 行則得 6 / 4 / 2,每行點數分別 1、2、0、1、1、1,archive.tar.gz 那一格一個人貢獻 2 個點、ext 卻只拿走 gz。

空行只進分母、不進任何計數:摻進 20000 行裡 5000 個空行,實測統計停在 15000,被跳過的 5000 不出現在任何一列。同分母這三處對齊,可以逐條核:

  • 表格行 11、破折號格 6、綠格 5,提示語的 N 也寫 11,四處同分母。
  • 示例灌的 6 行得 6 / 4 / 2,且 6 = 4 + 2。
  • 20000 行摻 5000 空行,統計 15000,空行不進任何一列。

至於同工具更早那篇拆兩列的文章,量的是另一套賬,和這裡切點的分支不是一回事,它的結論本篇不重複;要看那條鏈路可點開檔名拆兩列的舊文。

Q:photo. 明明有個點,怎麼被算成無後綴?

點在倒數第一位,substring(dot + 1) 從點後一位切到串尾是空字串,ext 為空,統計就把它歸進無後綴那一格;只是它的 name 被削掉了尾點,和 README 那種整串進 name 並不一樣。

Q:.tar.gz 不能整體當擴展名嗎?

在這一頁不能。切點恆在最後一個點,11 格裡 ext 含點 0 格,.tar.gz 的 ext 只剩 gz,前面那段留在 name 裡——工具文案裡「支援多擴展名」這句,和程式碼實際切出來的並不一致。

Q:第一列和輸入對不上,是壞了嗎?

它是按 name 拼回 ext 現造的重建串,不回填原串。尾點這一族因此在第一列少一個點,11 格裡有 4 格對不回修剪後的輸入,而第二列的 name 仍保留中間那些點。

📎 相關文章

3.7 讀成 3、1e3 讀成 1:分數計算器這一頁先在輸入端截成整數,再在 14 個字元處換顯示口徑 2026-11-14 正則測試頁永遠帶 g:填 gg 直接報錯、填大寫靜默失效,21 處帶組匹配只列 20 行 2026-11-14 星號段長寫死:脫敏頁五套掩碼固定切出 4、3、11、12 枚星號,18 碼元串被手機檔搶走、空框回落 * 2026-11-13 鎖是單向的、百分比是兩把尺:pct 50 得 619×430,PNG 四檔 quality 全 6971 B 2026-11-13 相對時間的「一個月」寫死 30 天:時間戳頁六檔除數、13 位毫秒輸入與 UTC+8/UTC-5 兩檔截斷實測 2026-11-13