「🤦🏼♂️".length == 7 並不是錯誤的 (2019) (★
100 分)
文章以「🤦🏼♂️」這個表情符號為例,說明為何在不同程式語言中,字串長度會出現看似矛盾的數字:在 JavaScript 中長度是 7,在 Python 3 中是 5,而在 Rust 中則為 17,但在 Swift 中卻只顯示為 1。原因在於這個表情實際上是由多個 Unicode 標量值 (Unicode scalar values) 組成,包括基本的「摀臉」符號、膚色修飾符、性別符號、零寬連接符 (Zero Width Joiner),以及變體選擇器,最後才渲染成使用者看到的一個表情。各語言回傳的「長度」其實反映了不同層次:UTF-8 編碼單元數、UTF-16 編碼單元數、Unicode 標量值數量,或是擴展字素叢集 (extended grapheme clusters) 的數量。
作者指出,雖然程式設計師在網路上常拿 JavaScript 的 `.length` 當笑話,但其實這個行為並非「錯誤」,因為不同語言的設計理念不同。像 Swift 強調人類直覺,回傳「1」代表「一個可見單位」;Rust 則強調效能和一致性,回傳 UTF-8 編碼單元數;而 Python 的實作比較混亂,雖然表面上是按 Unicode 標量值計算,但允許出現非正規的代理對 (surrogate pairs),因此被作者批評為「最糟」。文章進一步探討,每種「長度」在不同場合各有用處:記憶體配置時需要知道編碼單元數,UI 文字處理需要知道字素叢集,通訊協定有時以標量值長度設限,而真要估算顯示寬度,甚至還要考慮字體與 East Asian Width 等條件。
在公平性與國際化方面,文章舉了《世界人權宣言》的多語翻譯進行統計。許多人擔心 UTF-8 對中文、日文、韓文不公平,因為這些文字通常會佔 3 個位元組,但實際上漢字的資訊密度極高,因此同樣內容所需的 UTF-8 長度反而比許多以拉丁字母書寫的語言還短。韓文與日文的結果也顯示,存取長度差異背後更多是語言結構差異,而非編碼方式造成的不公。最後,作者強調「沒有一種量測方式能完美衡量字串長度」,程式語言應根據實際需求提供不同的計算方法,而不是追求單一「正確答案」。
在 Hacker News 討論區,使用者對此產生許多延伸思考。有些人注意到 `.length` 在技術上不同於「人看到的長度」,因此「沒有上下文的長度」其實毫無意義。他們認為應區分用途:若是資料庫或通訊,就需要位元組數;處理文字游標則需字素叢集;顯示則必須依賴字型渲染。而許多人認同 Rust 的做法:以 UTF-8 儲存並回傳位元組數,其他長度則透過迭代器額外計算。有開發者強烈批評 Python 3,認為經過十多年的轉換卻仍處理不佳;對比之下,某些語言如 Raku 提供了明確的 API,例如 `.chars`、`.codes`、`.bytes`,讓開發者清楚指定想要的資訊,避免混淆。
部分討論更聚焦在國際化與字型處理,例如德語「ß」在 Unicode 的大寫轉換問題,或泰文、梵文等文字的複雜組字行為。也有人提到 Wordle 等遊戲案例,說明「五個字母」可能在某些語言下難以界定。此外,有人認為「UTF-8 萬用」本身並非萬靈丹,因為隨 Unicode 更新,字素叢集判定仍可能不一致,導致不同版本環境下出現差異。整體而言,討論反映了社群共識:我們應接受「字串長度」沒有單一定義,而是在具體情境下選擇正確的計算方式,並設計合宜的 API,才是真正解決問題的方法。
👥
139 則討論、評論 💬
https://news.ycombinator.com/item?id=44981525