BOM 移除工具只會從解碼後的文字中精確移除一個位於前端的 U+FEFF 字碼單元——不會多也不會少,而且僅在該單一字元位於位置 0 時才會執行。在本文中,BOM 指的是 Unicode 字元 U+FEFF(位元組順序標記),它常出現在純文字片段的開頭——例如從 SAP GUI 複製的報表、ALV 匯出、spool 傾印,或任何其他在第一個真實字元前寫入 UTF-8、UTF-16 或 UTF-32 位元組簽章的文字來源。SAP 中同樣的三個字母也是 Bill of Materials(物料主檔)的縮寫,這是一個透過生產規劃交易維護的主資料物件,與本指南完全無關。搜尋 SAP 物料主檔相關內容的讀者應改為查看採購或工程文件路徑;遇到前置 U+FEFF 導致 JSON 解析器、SQL 載入器、shebang 或欄位名稱比對失敗的讀者,則能在下方找到所有需要的步驟。BOM 移除工具完全在當前的瀏覽器分頁中執行,絕不會上傳貼上的字串,並以位置作為唯一的規則。

本指南中 BOM 的含義
在 Unicode 中,縮寫 BOM 代表位元組順序標記,這是一個位於字碼點 U+FEFF 的單一字元,其可見字形為空,但其存在標誌著解碼後的字串是以編碼簽章開頭的。不同編碼的位元組簽章各不相同:UTF-8 檔案以三位元組序列 EF BB BF 開頭,UTF-16 以 FE FF 或 FF FE 開頭(取決於位元組順序),UTF-32 則使用對應的 32 位元標記。一旦解碼器將這些位元組轉換為字元,字串中剩下的就只有位於位置 0 的一個不可見 U+FEFF。SAP 中同樣的三個字母代表 Bill of Materials(物料主檔),這是在生產規劃模組中維護的主資料列;該語意存在於專屬的 SAP 交易中,與本工具無關。
在本文其餘部分,所有提到 BOM 的地方皆指 U+FEFF 字元——一個在解碼後仍然存活的不可見字碼點,可能會干擾那些預期第一個字元應為真實字母、數字、引號或 shell 標記的下游工具。
為何前置 U+FEFF 會破壞您的資料流
前置 U+FEFF 的問題在於幾乎沒有任何東西會將它列印、記錄或計算。您複製了看似 NAME,VALUE 的字串,將其貼到差異比對工具中,差異工具卻顯示第一欄缺失,因為 U+FEFF 隱形地坐在 NAME 前面。以下是跨技術堆疊反覆出現的幾種失敗模式:
- JSON 解析器(例如 JavaScript 的 JSON.parse)會以「Unexpected token」錯誤拒絕輸入,並指向第 1 欄,即使第一個可見字元是引號。
- CSV 讀取器會將 BOM 暴露為欄位名稱,使得第一列顯示為「\uFEFFheader_one」而非「header_one」,進而破壞引用該欄位的 SQL SELECT 陳述式。針對 CSV 專屬的匯出流程,Remove BOM from CSV Files Without Losing Data 工作流程會從頭到尾走過相同的規則。
- Shebang 行(例如 #!/usr/bin/env python)一旦在井字號前出現 U+FEFF,就會失去 shebang 的作用,POSIX 核心會拒絕執行該檔案。
- 兩個「看起來相同」的檔案之間的字串相等性檢查會失敗,因為其中一個以 U+FEFF 開頭,另一個則否,這會浪費程式碼審查與差異比對工具的時間。
- Shell 的 while read 迴圈會將 BOM 視為第一個欄位名稱的一部分,留下一個會破壞下游 awk 欄位分隔符的殘留隱形字元。
這些失敗都源自同一個根本原因:位置 0 不是消費者預期的內容。在解碼階段修正字串是最乾淨的做法;以受控方式剝除已解碼的字元是次佳方案,而 BOM 移除工具正是為這第二種情況所設計。
BOM 移除工具如何決定要刪除的內容
BOM 移除工具的邏輯刻意保持精簡。如 MDN 的 JavaScript 詞法文法參考所述,charCodeAt(0) 會回傳第一個 UTF-16 字碼單元的整數值,這正是工具唯一一個測試在頁面中執行判定的方式。工具會檢查該第一個值是否等於 0xFEFF。若是,工具會回傳 input.slice(1)——也就是移除了該單一字碼單元的原始字串。若第一個單元是其他任何值,工具會原封不動地回傳輸入字串。這裡沒有修剪、沒有正規化、沒有全域正則表達式、沒有 replaceAll,也不會掃描後續位置。
這條精簡的規則會產生三個明確的後果,原始資料中已明確指出:
- 字串內部的 U+FEFF 屬於內容。根據 Unicode 規範,非起始位置的 U+FEFF 過去具有零寬度不斷行空格語意,而現代建議改用 U+2060 WORD JOINER 來擔任該角色。剝除所有出現位置會摧毀合法的內容,因此工具刻意保留字串內部與結尾的 U+FEFF。
- 換行後的 U+FEFF 屬於內容。工具絕不會合併或重新調整換行符,因此 CRLF 後接 U+FEFF 的序列在下一行仍會包含前置 U+FEFF——這正是該規則設計上要跳過的內部內容類型。
- 兩個前置 U+FEFF 字碼點不等同於一個。Unicode 將第一個視為可能的編碼簽章,第二個視為普通的起始內容。BOM 移除工具僅移除第一個;第二個會滑入位置 0 並留在那裡,直到您再次執行工具。在重複執行操作之前,請先閱讀摘要。
實作刻意保持簡短,因為任何額外的檢查都可能將範圍擴大到超過單一字元,或冒著變更字串中其他合法隱形內容的風險。
逐步使用 BOM 移除工具
完整工作流程包含三個動作,所有步驟都在當前瀏覽器分頁中執行。開啟 BOM 移除工具,貼上您的字串,並依照以下順序操作。
- 將已解碼的文字貼入輸入區域。從您的 SAP GUI 視窗、ALV 匯出、記錄檔、終端機或任何其他來源複製片段,並將其完整貼上,包括不可見的前置 U+FEFF(若存在)。瀏覽器的文字區域儲存的是解碼後的字元而非原始位元組,因此工具只能看到解碼後的形式。
- 執行移除工具並閱讀狀態欄位。狀態會告訴您究竟移除了恰好一個前置 U+FEFF,還是未找到任何前置 U+FEFF。它也會回報以 UTF-16 字碼單元計算的完整輸出長度,讓您能確認內部內容並未被靜默修改。
- 檢視完整輸出,然後在本機複製。從頭到尾閱讀結果面板——換行符、內部 U+FEFF 內容、結尾的隱形字元、表情符號、代理對、組合標記等等。當您確認轉換結果符合預期時,將結果複製回目標檔案、編輯器或資料流中。若平台拒絕寫入剪貼簿,仍可手動選取唯讀輸出進行複製。
若您在同一輸出上再次執行工具,將會移除第一次執行留下的第二個前置 U+FEFF。對一個以兩個 U+FEFF 字元開頭的字串執行兩次,所產生的最終字串等同於一次移除兩者,但本工具絕不會在單次點擊中同時完成兩個動作。
BOM 移除工具保留與移除的內容
位置規則會產生八種不同的情境,每一種都有明確且可預期的結果。下表摘要列出原始資料中列舉的所有情況,讓您在點擊前能比對自己的輸入。
| 輸入以以下內容開頭 | 輸出 | 原因 |
|---|---|---|
| U+FEFF 後接內容 | 移除一個 U+FEFF,其餘不變 | 第一個字碼單元等於 0xFEFF,因此工具回傳 slice(1) |
| 任何其他字元 | 輸入的相同複本 | 第一個字碼單元不是 U+FEFF,因此工具回傳原始輸入 |
| 兩個 U+FEFF 字元 | 移除第一個;第二個留在位置 0 | Unicode 將第二個視為 BOM 之後的起始內容 |
| 可見內容內部的 U+FEFF | 保留於原始索引位置 | 規則絕不會掃描超過位置 0 |
| 換行(CRLF 或 LF)後的 U+FEFF | 保留於其所在行 | 不會對換行符進行正規化或掃描 |
| 結尾處的 U+FEFF | 保留於末端 | 不存在字串末端修剪或移除操作 |
| 空輸入 | 空輸出 | 空輸入是有記載的不變更情況 |
| 僅有一個 U+FEFF 且無其他內容 | 空字串 | 即使零可見字元,結果面板仍會渲染 |
輸入與輸出邊界
BOM 移除工具在兩端強制執行相同的數值上限:最多 200,000 個 UTF-16 字碼單元,對您貼上的輸入與工具回傳的字串皆獨立檢查。此邊界為包含式——剛好 200,000 個字碼單元的輸入會被接受,再多一個字碼單元會在處理開始前遭到拒絕。由於最多僅移除一個字碼單元,成功的操作絕不會讓字串變長;轉換後另外進行的輸出長度檢查讓此不變式可供測試。
當首字元為 U+FEFF 時,輸出長度等於輸入長度減一:output_length = input_length − 1。代入 input_length = 200,000 可得 output_length = 199,999 個字碼單元——仍遠低於文件層級上限。
這裡沒有靜默截斷、抽樣、substring(0, 200000) 之類的捷徑,也沒有悄悄裁剪文字區域的 maxLength 屬性。若您的字串未通過邊界檢查,結果面板會連同任何先前的成功輸出一起清除,如此一來,過時的結果便不會被誤認為當前結果。綜合來看,這些規則為您提供三項保證:被接受的輸入會送達邏輯、被接受的輸出反映轉換結果,而被拒絕的輸入根本不會產生部分輸出。
僅限瀏覽器處理與剪貼簿處理
資料不會離開此分頁。貼上動作寫入頁面自身的狀態,轉換針對 input.slice(1) 執行,結果存放在由您控制的唯讀面板中。貼上的文字不會上傳至後端,也不會被傳送至外部服務。
剪貼簿存取是非同步的,因此複製按鈕會將請求與一個世代識別碼配對。編輯輸入、清除、重試或離開頁面都會使該世代失效,這代表延遲回應的剪貼簿權限無法將過時的「已複製」狀態貼到已變動的畫面上。若瀏覽器拒絕剪貼簿權限,輸出面板仍會保持可見且完全可選取,讓您始終有手動複製的替代途徑。
對於非常大的字串,建議每個來源只執行單一貼上並複製的循環,以便非同步世代與您在畫面上實際想看到的狀態相符。
解碼器何時才是正確的第一步
BOM 移除工具的功能刻意設計得很狹隘。它無法檢查檔案的原始位元組,因此也無法判斷您的文字原本是 UTF-8 (EF BB BF)、UTF-16 (FE FF 或 FF FE),還是 UTF-32 (FF FE 00 00 或 00 00 FE FF)。根據 WHATWG 編碼即時標準 (Living Standard),BOM 政策的責任歸屬於瀏覽器自身的解碼器;若您掌控檔案載入流程,請在字串傳遞至任何文字欄位之前,依應用程式需求設定解碼器,選擇去除或保留 BOM。
在以下情境下使用 BOM 移除工具:
- 可信賴的解碼器已產生一段字串,而其開頭的 U+FEFF 正在干擾剖析器、比較、shebang、欄位名稱或 shell 管線。
- 您希望在把結果貼回原始碼管控或目的地系統之前,快速且無需上傳地確認已精準移除一個不可見字元。
- 您需要刻意重複此操作 —— 例如,在某個以兩個前置 U+FEFF 字元開頭的字串中,您希望移除第一個簽章,卻保留第二個內容字元。
請勿將 BOM 移除工具用於:
- 從貼上的片段推測檔案原本的編碼。
- 將原始 UTF-16 位元組轉換為任何東西 —— 此工具永遠不會接觸到位元組。
- 修復亂碼、規範化空白,或從串接的文件中去除所有 BOM 類字元。
- 改寫那些首位字元本來就刻意設計為 U+FEFF 的文字 —— 此工具會將其移除。
若前置的 U+FEFF 在您的內容層中是刻意的,請保留它。若屬非預期,請將字串貼入 BOM 移除工具、讀取狀態,並在覆蓋原始內容之前,先在本機複製清理後的版本。
若您正在權衡各種選項,如何從貼上的文字中精準去除一個前置 BOM 一文對此有詳細說明。