bommer 是 BOM 的諧音誤拼,BOM 是 Unicode 的 U+FEFF 位元組順序標記,在解碼後的文字中可能以隱形首字元的形式出現。若要徹底移除 bommer,請將已解碼的文字貼到 BOM Remover,它只會檢查貼上字串的第一個 UTF-16 程式碼單元,若 U+FEFF 位於位置零,就會精準移除一個。接著工具會回報是否找到前導 bommer,傳回包含所有內部與尾端 U+FEFF 字元的完整輸出,並在本機複製結果,不會將輸入上傳到任何服務。整個處理過程完全在當前的瀏覽器分頁中進行,因此原始檔案編碼、未經處理的位元組序列,以及解碼器的行為對工具來說都是不可見的——工具只會檢查文字區塊所產生的解碼後 JavaScript 字串。這種嚴格的、僅依位置判斷的規則,正是 BOM Remover 能安全用於 JSON、CSV、shell 指令稿、SQL 傾印、設定檔,以及任何開頭若出現多餘隱形字元就會破壞剖析器、shebang、欄位名稱或字串比較的文字的原因。

how to remove bommer spring hinges
如何從貼上的文字中移除 Bommer 字元

「bommer」在文字檔中真正的含義

「bommer」這個詞在 Unicode 標準或任何編碼規範中都不存在。實際上,它會出現在搜尋查詢和支援單中,當作 BOM(位元組順序標記)的諧音拼法,而 BOM 是 Unicode 指派給碼位 U+FEFF 的字元。這個字元在大多數編輯器中是隱形的,單獨呈現時的推進寬度為零,且通常會在文字串流的開頭出現——當解碼器認可如 UTF-8 的 EF BB BF,或 UTF-16 的 FE FF、FF FE 等位元組簽章時就會發生。由於這個字元本身不會繪出任何字形,使用者會看到第一行壞掉、剖析器在第一個字元就拋出例外,或是字串比較莫名其妙地失敗,於是就用腦中浮現的任何詞來形容這種症狀——「bommer」、「bommer 彈簧鉸鏈」、「bommer 側邊護蓋」,或單純說「看不見的首字元」。

Unicode 的指引說明,在串流開頭的 U+FEFF 是被允許的簽章;而非開頭的 U+FEFF 歷史上帶有零寬不中斷空格語意,這也是為什麼同一個碼位會依出現位置而有兩種不同含意。新的零寬連接內容偏好的碼位是 U+2060 WORD JOINER,但較舊的 U+FEFF 行為仍存在於真實文字中。這種雙重含意正是一般「移除所有隱形字元」做法具有破壞性的原因,也正是位置感知工具才是正確做法的理由。

為何前導 U+FEFF 會破壞剖析器與比較

在解碼後文字的開頭出現一個 U+FEFF,會悄悄讓所有預期第一個字元為真實字母、數字、符號或換行的東西失效。JSON 剖析器會拒絕第一個解碼字元不是空白、陣列方括號或物件大括號的輸入。以嚴格 RFC 4180 假設所建構的 CSV 匯入工具,可能會把第一欄的標頭位移一個字元,讓檔案看起來像多了一個空白的開頭欄。帶有 shebang 的 shell 指令稿在前三個位元組為 EF BB BF(UTF-8 BOM)時會失敗,因為在位元組位移 0 尋找 '#!' 的直譯器永遠找不到 shebang 那一行。SQL 載入器、YAML 發射器、XML 處理器,以及支援 Heredoc 的工具都有相同的盲點:第一個字元是特殊的,而隱形的字元是最糟糕的一種特殊。

字串比較也會遭遇同樣的命運。「開頭為」檢查、排序後的清單、去除重複的索引鍵,以及主鍵查詢,全都可能把兩個本質相同的字串視為不同,只因為其中一個的開頭是 U+FEFF。這個 bug 很少被察覺,直到有人從兩個來源貼上相同的值,並發現去除重複的結果差了一個。移除開頭的 bommer 是唯一安全的修正方式,而且只有在你能證明那個前導字元確實是多餘的情況下才適用。

使用 BOM Remover 移除 bommer

從貼上的解碼文字中移除 bommer 的已驗證工作流程刻意設計得很短。每一步都只需單一瀏覽器互動,且工具會以純文字呈現結果,讓你在複製前可以先行檢視。

  1. 將已解碼的文字貼到輸入區域中,若開頭有隱形的 U+FEFF 請一併包含。工具作用於文字區塊所產生的解碼後 JavaScript 字串,因此請勿貼上原始位元組並期待它能做到位元組層級的偵測。
  2. 執行移除工具並讀取狀態列。摘要會明確說明是否恰好移除了單一前導 U+FEFF,或是根本沒有找到前導 U+FEFF,並以 UTF-16 程式碼單元回報完整輸出的長度。
  3. 檢視整個輸出,包括換行字元以及任何刻意存在的後置 U+FEFF 內容。輸出面板即使在輸出為空時仍會顯示,避免將「僅 BOM 移除成功」誤判為操作失敗。
  4. 使用頁面內的複製控制項在本機複製清理後的文字。剪貼簿寫入動作受到世代識別碼的保護,因此延遲或被拒絕的權限回應不會留下過時的「已複製」狀態,且完整的唯讀輸出仍可供手動選取。

若你的輸入在開頭包含兩個連續的 U+FEFF 碼位,BOM Remover 只會移除第一個;第二個會留在輸出的開頭,因為 Unicode 允許它代表初始的零寬不中斷空格。此時對輸出再次執行工具,就會移除這個現已成為前導的第二個 U+FEFF,因此重複操作前請務必重新閱讀摘要。

BOM Remover 對結果強制執行的規則

BOM Remover 刻意做得範圍很窄,而這種窄正是輸出值得信賴的原因。實作並不會對你的輸入呼叫 trim、trimStart、replaceAll 或全域正規表達式。它只會比對 input.charCodeAt(0) 與十六進位值 FEFF,並在相符時回傳 input.slice(1)。當第一個程式碼單元是其他任何值時,所回傳的字串就會是與輸入完全一致、未經任何修改的字串。因此,你文字中的每一個其他字元——包括 CRLF 與 LF 換行、Tab、NUL 位元組、表情符號、組合記號、代理對、非拉丁文腳本,以及任何後續出現的 U+FEFF——都會逐位元組保留。編輯或清除輸入會立即清空先前的輸出、錯誤訊息、摘要、移除旗標、複製訊息,以及任何待處理的複製計時器,因此「超過上限」錯誤不會讓舊的成功結果殘留在畫面上。

獨立的輸出驗證會強制設定硬性上限:輸入最多可包含 200,000 個 UTF-16 程式碼單元,輸出也有獨立的 200,000 程式碼單元上限。由於轉換過程最多只會移除零或一個程式碼單元,合規的實際輸入在處理期間不可能膨脹,因此輸出檢查仍維持為明確的不變式與可執行的測試邊界。剛好等於上限的輸入會被接受;再多一個程式碼單元就會在處理前被拒絕。不會有任何縮短、取樣或部分回傳的情況,也不會有 HTML maxLength 屬性悄悄擋下任何東西。

BOM Remover 不會做的事

位置是唯一的規則,許多聽起來合理的任務明確落在範圍之外。BOM Remover 無法從貼上的文字推斷原始檔案編碼,因為瀏覽器的文字區塊早已包含解碼後的字元,工具只能判斷結果字串是否以 U+FEFF 開頭。它無法告訴你來源檔案曾經包含 EF BB BF、FE FF 或 FF FE;當文字送達工具時,那些位元組簽章早已消失。它不會轉換 UTF-16 位元組、驗證 UTF-8、修復亂碼 (mojibake),也不會去除任意的零寬字元,例如 U+200B、U+200C、U+200D 或 U+2060。它不會從串接的文件中去除所有類 BOM 字元,因為去除所有內部 U+FEFF 會悄悄刪除 Unicode 標準認定為文字一部分的內容。

若你負責檔案載入,正確的第一步是選擇具有合適 BOM 政策的解碼器——許多 JSON 與 CSV 讀取器都提供切換開關——而不是把檔案的解碼後內容貼到這裡。若你將文字貼到 BOM Remover,請在將結果覆寫到原始內容之前,先確認前導 U+FEFF 確實是不需要的,因為切片操作在輸出緩衝區中是精準且不可逆的。如需更廣泛、以檔案為導向的工作流程,你可以將本做法與 如何在瀏覽器中移除文字的前導 BOM 相互比較,後者描述了相同的位置限定規則在檔案貼上情境下的應用。

BOM Remover 與一般文字清理方法的比較

許多通用文字工具會毫不猶豫地去除每一個找到的 U+FEFF、所識別的每個零寬字元,或前 3 個位置中 EF BB BF 範圍內的每個位元組。這些行為聽起來很貼心,直到它們與正當含有非初始 U+FEFF(作為零寬不中斷空格)的內容相衝突。下表摘要說明僅依位置判斷的移除工具,與全域清理工具在安全文字編輯相關軸向上的差異。

行為 BOM Remover(僅依位置) 全域隱形字元清理工具
可移除的最大 U+FEFF 數量 恰好一個,且只在索引 0 字串中的所有出現位置
內部 U+FEFF 是否保留 是,逐位元組保留 否,作為全域處理的一部分一併移除
尾端 U+FEFF 是否保留 是 通常不會
兩個連續的前導 U+FEFF 移除第一個,第二個留在位置 0 兩者皆移除
其他零寬字元 (U+200B, U+200C, U+200D, U+2060) 超出範圍,不予處理 通常會被去除
網路上傳 無,在當前分頁執行 視工具而定
輸入與輸出的大小限制 200,000 個 UTF-16 程式碼單元,獨立檢查 不定,通常為隱含式

這兩個欄位描述的是不同的合約。當你只懷疑前導 U+FEFF,並希望保留其他所有位元組時,請使用 BOM Remover。僅在你已獨立確認文字不含合法的零寬不中斷空格、不含合法的內部 U+FEFF,且不含同一字元在不同段落中具有不同意義的串接文件時,才使用全域清理工具。如需另一種強調檔案層級而非字元層級檢視的貼上並清理路徑,在瀏覽器本機的指南 如何在瀏覽器中於本機移除檔案的 BOM 中,會逐步說明 BOM Remover 實作所引用的相同 JavaScript 詞法語法邊界;而如何精準去除貼上文字中單一前導 BOM 則會從稍有不同的起點再次闡述僅依位置判斷的規則。

Practical checks before and after

在你貼上之前,請確認你的解碼器沒有悄悄地為你移除掉一個 BOM 字元 — 有些載入器會識別 UTF-8 簽章並自動將其移除,在這種情況下 BOM Remover 會回報找不到前導的 U+FEFF,且輸出將等於輸入。在你複製之後,請確認清理過的文字在目的地工具中仍然可以解析,並將回報的輸出長度與你的預期進行比對:如果你的輸入是一個 U+FEFF,則有效的輸出為空,且結果面板仍會以長度為零的方式顯示。如果你的輸入有兩個前導 U+FEFF,則輸出仍會以一個 U+FEFF 開頭,這在 Unicode 規則下是正確的,因為它會將第二個碼位視為初始內容。如果你也需要移除該第二個字元,請將結果再次貼回工具中並再執行一次 — 摘要會告訴你又移除了一個前導的 U+FEFF,接著你可以決定是要在該處停止,還是將剩餘的字元視為內容來看待。

為了從瀏覽器端確認被移除的字元確實是 U+FEFF,而不是視覺上相似的零寬度空格,MDN JavaScript lexical grammar 參考文件記載了 JavaScript 字串如何將 U+FEFF 視為一個空白字元碼位,而 BOM Remover 則改為使用精確的零號位置檢查,並不會將該空白規則套用於後續的字元。位於 encoding.spec.whatwg.org 的 WHATWG Encoding Living Standard 說明了各種解碼器如何與位元組簽章互動,這就是 BOM 字元最初進入文字串流的上游層級。這些參考資料共同說明了為何此工具是檢查字元而非位元組,以及為何它無法從貼上的文字區中推斷出原始編碼。