文字與寫作 指南

明明看起來一樣,為什麼搜尋、排序或比對失敗?

因為螢幕上的『空白』不一定都是 U+0020 SPACE;有些 Unicode 字元甚至完全沒有可見寬度。

更新日期:

兩個字串:

Hello World

看起來完全一樣。

但其中一個中間可能是:

  • 普通 SPACE
  • NO-BREAK SPACE

人眼看不出來。

程式卻知道:

code point 不同。

普通 SPACE

最常見:

U+0020 SPACE

一般英文輸入空白。

NO-BREAK SPACE

U+00A0 NBSP

主要目的:

避免在這個位置換行。

常見來源:

  • 網頁
  • rich text editor
  • copy/paste
  • CMS

看起來像普通空白。

但搜尋或 exact compare:

可能不同。

IDEOGRAPHIC SPACE

U+3000

常見於 CJK 排版。

寬度通常更接近:

全形。

如果資料欄位要求 ASCII space:

它不是同一個字元。

ZERO WIDTH SPACE

U+200B

名稱有 space,但:

通常沒有可見寬度。

Unicode 說明它可作為隱藏的 break opportunity。

所以:

abc​def

看起來像: abcdef

實際卻多一個 code point。

ZERO WIDTH JOINER

U+200D ZWJ

它不是一般「空格」。

常用來連接:

  • emoji sequences
  • complex scripts

如果粗暴地:

刪除所有 zero-width characters

可能破壞 emoji 或某些文字。

所以不能把所有 invisible Unicode 一律當垃圾。

為什麼會造成 bug?

Login / username

使用者貼上後可能含 invisible chars。

Excel / CSV

兩個值看起來相同:

exact match 卻 false。

Sorting

排序位置可能異常。

Character limit

看起來沒有增加:

count 卻增加。

Search

Ctrl+F 找不到。

怎麼檢查?

第一步:

看 character count。

若視覺內容沒變,但 count 比預期多:

懷疑 hidden chars。

第二步: 把文字貼到:

  • code editor
  • Unicode inspector
  • hex/code-point viewer

FunnyTools 現行 Character Counter 目前顯示長度與 UTF-8 bytes,但不是專門的 Unicode code-point inspector。

所以不要宣稱它能列出每個 hidden code point。

Remove Empty Lines 能處理什麼?

它會把:

只含 whitespace 的整行

當作空白行。

但這不代表:

可以清掉任意文字中所有 invisible Unicode。

這是不同問題。

最安全的 normalization 方法

先定義用途。

一般文章

不要任意刪 ZWJ。

名單

可以考慮:

  • trim
  • normalize known spaces

但先保留原始資料。

Identifier

一定要依系統規格。

例如:

username 是否允許 Unicode?

應由 application validation 定義。

常見錯誤

所有看不見字元都刪掉

危險。

NBSP 就是一般空白

視覺近似,但 code point 不同。

ZWJ 就是多餘空格

錯,它是 joiner。

UTF-8 byte 多一點就是惡意字元

不一定。

相關指南

建立可追蹤的 Unicode 診斷

遇到看不見的差異,可將字串中的每個位置標成索引、字元名稱、十六進位 code point 和 UTF-8 bytes。這份診斷只用於除錯,不應直接公開可能含有密碼或個資的原文。確認問題後,制定最小替換規則,例如只把 NBSP 轉成一般空白,或只移除來源明確加入的 BOM。每次替換都保留前後長度與測試結果,並用相同案例驗證搜尋、排序、複製和 API 比對,避免一次清掉所有特殊字元而引入新錯誤。

找出問題而不是盲目刪除

看不見的字元先要被分類。一般空白、NBSP、tab、LF、CRLF、zero-width space、zero-width joiner 和 BOM 的用途不同;有些是排版分隔,有些是檔案標記,有些負責把 emoji 組合成一個顯示圖案。可以把輸入轉成 code point 或 UTF-8 bytes 的診斷表,標出位置、名稱與前後文,再決定是否清理。

若問題是搜尋不到、比對失敗或 API 拒絕,先建立最小重現:原始字串、可見顯示、預期結果、實際 code point 和目標系統規則。對密碼、簽章、程式碼與使用者輸入,不要把所有零寬字元一律刪掉;這可能改變安全資料或讓問題無法追查。清理後重新計算長度,並在瀏覽器、匯出檔與實際 API 各驗證一次,確定改動只處理已確認的噪音。

輸入驗證與顯示要分開

清理工具可以協助定位異常字元,但不應默默改寫所有輸入。對登入名稱、檔案路徑、API key 或資料庫欄位,先建立允許字元和正規化規則,遇到不符合時回報位置與 code point,讓使用者決定是否修正。對一般文章則可提供預覽差異,讓使用者比較原文與清理後版本,再手動確認標題、網址、數字和標點沒有被改變。

相容性測試要覆蓋複製貼上、不同瀏覽器、CSV 匯入、搜尋比對與排序。輸入端若做 Unicode normalization,輸出端也要使用一致規則,否則兩個看似相同的字串仍可能無法比對。記錄清理前後長度、bytes 和異常位置,能幫助工程人員分辨編碼問題、不可見空白和真正的資料內容,而不是只看到一個模糊的「不相等」。

建議的診斷輸出

診斷畫面可以逐字列出索引、可見替代符號、Unicode 名稱、code point、UTF-16 長度和 UTF-8 bytes,但原文應提供遮罩或只在本機顯示。對於一段短字串,讓使用者能點選差異位置並複製單一 code point;對大量內容則只報告異常數量、第一個位置與前後幾個安全片段,避免把個資或密鑰寫入日誌。

修正規則也要有明確名稱,例如「只將 NBSP 轉為 SPACE」或「移除檔案開頭 BOM」,不要使用含糊的「清除特殊字元」。每個規則建立保留案例與拒絕案例,測試英文、中文、組合符號、網址和程式字串。當規則更新時,比較前後診斷報告,確認新增的替換範圍是有意義且可回復的。