Go1.18 新特性:棄用 strings.Title 方法,換個新坑吧!

煎魚發表於2022-02-22

大家好,我是煎魚。

最近在看 Go1.18 Release Notes 時,發現 strings, bytes 標準庫的 Title 方法,竟然被棄用了(Deprecated),這是為什麼呢?

今天這篇文章就由煎魚和大家一起看看。

介紹

這裡以 strings 標準庫為例,strings.Title 方法的作用是:將所有單詞開頭的 Unicode 字母對映到其 Unicode 標題大小寫。

例子如下:

import (
    "fmt"
    "strings"
)

func main() {
    fmt.Println(strings.Title("her royal highness"))
    fmt.Println(strings.Title("eddy cjy"))
    fmt.Println(strings.Title("хлеб"))
}

輸出結果:

Her Royal Highness
Eddy Cjy
Хлеб

這些單詞均被轉換為其大寫。

問題

看上去似乎一切都很美好,但其實他現階段有 2 個明顯的缺陷。

分別是:

  • 無法正確處理 Unicode 標點符號。
  • 不考慮特定人類語言的大寫規則。

接下來我們具體展開講講。

Unicode 標點符號

第一個問題,例子如下:

import (
    "fmt"
    "strings"
)

func main() {
    a := strings.Title("go.go\u2024go")
    b := "Go.Go\u2024Go"
    if a != b {
        fmt.Printf("%s != %s\n", a, b)
    }
}

輸出結果:

Go.Go․go != Go.Go․Go

變數 a 轉換處理的結果是 “Go.Go․go”,但按照實際的訴求應當為 “Go.Go․Go”。

特定語言規則

第二個問題,程式碼如下:

func main() {
    fmt.Println(strings.Title("ijsland"))
}

輸出結果:

Ijsland

在荷蘭語的單詞中,“ijsland” 應大寫為 “IJsland”,但結果轉換為 “Ijsland”。

解決方案

這個問題在 2013 年就發現了,來源於《strings: Title function incorrectly handles word breaks》,被 Go 語言之父 Rob Pike 標識為計劃外的問題。

如下圖:

由於 Go1 相容性保障的條約,因此這是 “無法” 修復的,一旦修復就會影響函式的輸出結果,是破壞性變更。

但也可以採取別的方式,也就是本文中提到的 “棄用”。如下標識:

// Title returns a copy of the string s with all Unicode letters that begin words
// mapped to their Unicode title case.
//
// BUG(rsc): The rule Title uses for word boundaries does not handle Unicode punctuation properly.
//
// Deprecated: Use golang.org/x/text/cases instead.
func Title(s string) string {

在函式上標識 “Deprecated”:

https://pkg.go.dev

對應 Go 文件會將其摺疊並明確顯示棄用,建議直接使用 golang.org/x/text/cases 庫來實現該功能。

新的 x/text/cases 案例如下:

import (
    "fmt"

    "golang.org/x/text/cases"
    "golang.org/x/text/language"
)

func main() {
    src := []string{
        "hello world!",
        "i with dot",
        "'n ijsberg",
        "here comes O'Brian",
    }
    for _, c := range []cases.Caser{
        cases.Lower(language.Und),
        cases.Upper(language.Turkish),
        cases.Title(language.Dutch),
        cases.Title(language.Und, cases.NoLower),
    } {
        fmt.Println()
        for _, s := range src {
            fmt.Println(c.String(s))
        }
    }
}

輸出結果:

hello world!
i with dot
'n ijsberg
here comes o'brian

HELLO WORLD!
İ WİTH DOT
'N İJSBERG
HERE COMES O'BRİAN

Hello World!
I With Dot
'n IJsberg
Here Comes O'brian

Hello World!
I With Dot
'N Ijsberg
Here Comes O'Brian

輸出了多種語言的轉換,我們核心關注 cases.Lower(language.Und) 相關的程式碼,該庫會通過呼叫:

  • cases.Title(<language>).Bytes(<bytes>)
  • cases.Title(<language>).String(<string>)

在程式設計中指定處理的語言,來解決不同人類語言的符號、不同語言和大寫詞語的訴求,避免一刀切。

總結

雖然只有一個小小的函式,但也延伸出了不少的問題。本質上還是在設計時,存在認知的侷限性。

另外 strings.Titlebytes.Title 函式,在實際工作中也常被誤解為就是轉換首字母大寫的方法,與設計含義相違背。

雖然最終與缺陷相比,這樣的誤解卻帶來了更好的效果,但對於一些特殊場景和語言支援,還是有很大的問題。

也算是塞翁失馬,焉知非福了。

若有任何疑問歡迎評論區反饋和交流,最好的關係是互相成就,各位的點贊就是煎魚創作的最大動力,感謝支援。

文章持續更新,可以微信搜【腦子進煎魚了】閱讀,本文 GitHub github.com/eddycjy/blog 已收錄,學習 Go 語言可以看 Go 學習地圖和路線,歡迎 Star 催更。

相關文章