雜記 › 技術筆記 › 副檔名從最後一個點算起:LICENSE、.gitignore、archive.tar.gz 為什麼會漏改

副檔名從最後一個點算起:LICENSE、.gitignore、archive.tar.gz 為什麼會漏改

有次把一批圖片交給伺服器,靜態規則裡只寫了小寫後綴,photo.JPG 和 document.PDF 整批漏在那條規則外面。想統一成小寫,我把二十幾個名字貼進去先跑了一輪,結果分三種:一半名字被改、幾個原樣吐回來、archive.tar.gz 只改了後半截。漏改這一類是會重複發生的,所以更值得先弄清楚。

先擺尺度:三種模式對同一行給出不同答案的行數是 20、19、20,佔 74.1%、70.4%、74.1%——選哪一檔並不是口味問題。

後綴從哪個點算起:最後一個,而且下標要大於 0

判定只有兩行:var dot = filename.lastIndexOf('.'); 然後 if (dot <= 0) 就直接回傳原字串、後綴記為空。dot 是下標,不是個數,整行沒有點時是 -1,點在最前面時是 0,這兩種都被那道判斷擋在轉換之外。

輸入最後一個點取到的後綴UPPERlowerTitle
photo.JPG5JPG未變化photo.jpgphoto.Jpg
archive.tar.gz11gzarchive.tar.GZ未變化archive.tar.Gz
.profile.bak8bak.profile.BAK未變化.profile.Bak
.gitignore0空未變化未變化未變化
LICENSE-1空未變化未變化未變化
file.4空未變化未變化未變化
data.JSONL4JSONL未變化data.jsonldata.Jsonl
C:\Program Files\my.app\run19app\run目錄段被當後綴未變化同樣被改

「無後綴」那 7 行能拆開對得上號:整行沒點的 3 行(LICENSE、index、C:\path\to\file)、點在下標 0 的 2 行(.gitignore、.)、切完後綴是空字串的 2 行(file.、..),3 + 2 + 2 = 7,與統計欄報的數一致。尾巴那個點也不會被刪:file. 輸出仍是 file.。

archive.tar.gz dot 11,只動 gz index.html.bak dot 10,只動 bak 報告 2024.final.docx dot 13,final 不動 my.app\run dot 19,後綴 app\run .gitignore dot 0,整條早退 file. dot 4,後綴空 LICENSE 無點,原樣回傳

圖 1|最後一個點的下標,長條按下標等比;淺色三條都落在不轉換那一側。

三個模式改動的行數差這麼多,無後綴還不是第四類

UPPER 已轉換 11 行 40.7% UPPER 未變化 16 行 59.3% lower 已轉換 9 行 33.3% lower 未變化 18 行 66.7% Title 已轉換 19 行 70.4% Title 未變化 8 行 29.6% 無後綴 7 行 25.9%

圖 2|28 行清單裡有 27 行有效,長條按行數等比;無後綴在三檔都是 7。

計數口徑要先說明白:已轉換 + 未變化 = 總檔案數,11 + 16 = 27 成立;無後綴同時被加進兩個計數器,它是未變化的子集,四個數字加起來會比總檔案數大。貼進去 28 行、表格只有 27 行,缺的是那個空行,它連表都進不去。我是拿後綴大小寫轉換把同一份清單按三檔各跑一遍才把這些數字對上,頁面上寫著「已處理 27 個檔名」,重複的兩行 photo.JPG 都算進去了,工具不去重。

Title 這一檔最容易被當成「好看一點的小寫」,其實它會改寫後綴裡除首字元外的每一個字母:document.PDF 得 document.Pdf,data.JSONL 得 data.Jsonl。PDF、JPEG、MP3 這類縮寫想原樣留著只有 UPPER 做得到;後綴只剩一個字元時 Title 和 UPPER 等價(a.b.c.D 輸出仍是 a.b.c.D)。名字主體(最後一個點之前那一段)一律不動;整行文字的大小寫是另一件事,要統一可以先過一遍大小寫轉換。

貼整行路徑進來,被改的是目錄名片段

判定只找點,不認識反斜線。C:\path\to\file 沒有點,原樣透傳;目錄名裡帶一個點就露餡:C:\Program Files\my.app\run 的 dot 是 19,取到的「後綴」是 app\run,UPPER 之後變成 C:\Program Files\my.APP\RUN——改到的是目錄,不是副檔名。所以我現在只貼純檔名,整條路徑先交給從路徑裡取檔名。中文與全形字元不受影響:年度报告.docx 變 年度报告.DOCX,报告 2024.final.docx 裡那個 final 不動。

長度也不是問題:135 碼元的名字不截斷,換 400 行進去表格就出 400 行,原始碼裡沒有行數或位元組上限。

貼出來的文字和三處對不上的地方

  • 行首尾空白會被裁掉,兩處各 trim 一遍;note.txt 後面那個空格不會跟著回來,中間空格留著(mid dle.TXT 原樣)。
  • 空行、純空格行、純定位字元行整行消失且不補位置;行序不改,z.txt 排第一就一直排第一。
  • Windows 保留名和非法字元一概不校驗,CON.txt 照變 CON.TXT,PRN.log、nul.md 同理;a<b>|".txt 也能改,表格裡那對尖括號是轉義後顯示的。
  • CRLF 貼進去,行尾的 CR 在切行時就被吃掉,複製回來的永遠是 LF 行尾,不回寫 CRLF。

還有兩處要自己盯。一是介面不會告訴你哪行沒動:渲染那段有一個「前後是否相同」的判斷,但兩個分支拼出來的是同一個 fc-after 單元,實測三檔表格裡 fc-after 都是 27 次、fc-same 出現 0 次,已轉換與未變化長得一模一樣,只有統計數字能區分。二是這頁只處理文字,不碰磁碟也不上傳——腳本裡網路與本機儲存的呼叫都是 0 次,改名那一步要自己配 rename 或 mv。

Q:archive.tar.gz 為什麼只改了一半?

因為點只認最後一個:dot = 11,切出來的是 gz,tar 在它前面,屬於檔名那一段。三檔都只作用在最後一段上,想把 tar 一起改的開關並不存在。

Q:LICENSE 和 .gitignore 為什麼一行都不動?

同一條早退判斷:dot <= 0。LICENSE 整行沒有點,dot 是 -1;.gitignore 的點在下標 0。兩條都記成「無後綴」,但仍計入未變化那一欄,所以統計裡看得見它們。判定只看最後一個點的位置,不看開頭:.profile.bak 的 dot 是 8,就正常轉成 .profile.BAK。

Q:三種模式該按什麼選?

按要保留什麼選。要 URL 與伺服器慣例統一成小寫就走 lower,這份清單它只改 9 行;要保住 JPG、PDF 這種縮寫原樣只有 UPPER;Title 看著整齊,代價是把 PDF 拆成 Pdf。跑之前先放幾個代表名(雙層後綴、點開頭、無點各一個)各檔試一遍,比整批改完再回頭核對便宜。

📎 相關文章

PNG 壓了三遍只減 426 位元組:無損壓縮的真實邊界 2026-10-22 WebP 轉 PNG 後體積漲了幾倍:什麼時候必須轉,怎麼把漲幅壓住 2026-10-22 截圖縮到四分之一,小字糊成一團的三種原因 2026-10-22 同樣裁 300×300,頭像為什麼總偏右下角半格 2026-10-22 按每月 1.7 倍預估 18 個月,腳本和表格為什麼給出兩個數 2026-10-22