Go1.18 新特性:弃用 strings.Title 方法,换个新坑吧!

2022年2月15日 372点热度 0人点赞 0条评论

大家好,我是煎鱼。

最近在看 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[1]》,被 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>)

在编程中指定处理的语言,来解决不同人类语言的符号、不同语言和大写词语的诉求,避免一刀切。

但这个新 “坑”,显然也引入了更多的复杂性,说好的 “less is more...”,使用该方法时值得考量新的成本了。

总结

虽然只有一个小小的函数,但也延伸出了不少的问题。本质上还是在设计时,存在认知的局限性。

另外 strings.Titlebytes.Title 函数,在实际工作中也常被误解为就是转换首字母大写的方法,与设计含义相违背。

虽然最终与缺陷相比,这样的误解却带来了更好的效果,但对于一些特殊场景和语言支持,还是有很大的问题。

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

参考资料

[1]

strings: Title function incorrectly handles word breaks: https://github.com/golang/go/issues/6801

关注煎鱼,获取业内第一手消息和知识 ?

图片

图片

你好,我是煎鱼,出版过 Go 畅销书《Go 语言编程之旅》,再到获得 GOP(Go 领域最有观点专家)荣誉,点击蓝字查看我的出书之路

日常分享高质量文章,输出 Go 面试、工作经验、架构设计,加微信拉读者交流群,和大家交流!

73040Go1.18 新特性:弃用 strings.Title 方法,换个新坑吧!

这个人很懒,什么都没留下

文章评论