Go语言 strings.Title() is Deprecated:如何使用 cases.Title() 实现首字母大写

Golang 使用 cases.Title 实现多语言环境下正确的首字母大写

文章目录

在 Go 语言的开发过程中,我们经常需要对字符串进行各种操作,例如将字符串的首字母大写。过去,我们可能会使用strings.Title()函数来完成这个任务。然而,在 Go 1.18 版本中,strings.Title()被标记为已废弃(Deprecated),官方建议使用cases.Title()来代替。那么,为什么会有这样的变化呢?两者之间有什么区别?cases.Title 如何使用呢?

为什么strings.Title()被废弃?

先看下 strings.Title 的实现代码:

// Title returns a copy of the string s with all Unicode letters that begin words
// mapped to their Unicode title case.
//
// Deprecated: The rule Title uses for word boundaries does not handle Unicode
// punctuation properly. Use golang.org/x/text/cases instead.
func Title(s string) string {
	// Use a closure here to remember state.
	// Hackish but effective. Depends on Map scanning in order and calling
	// the closure once per rune.
	prev := ' '
	return Map(
		func(r rune) rune {
			if isSeparator(prev) {
				prev = r
				return unicode.ToTitle(r)
			}
			prev = r
			return r
		},
		s)
}

在代码注释中提到:

Title 返回字符串 s 的副本,副本中的所有单词开头的 Unicode 字母都会被映射为其 Unicode 首字母大写形式。

已废弃:Title 使用的单词边界规则无法正确处理 Unicode 标点符号。 请改用 golang.org/x/text/cases。

strings.Title 方法在 Go 1.18 后被标记为废弃,主要是因为它在处理 Unicode 标点符号和单词边界时存在问题。这个方法假设单词边界仅由空白字符决定,但实际情况要复杂得多,尤其是 Unicode 中的标点符号和其他符号可能影响单词边界的定义。

在 Unicode 标准中,单词边界的规则不仅包括空白符,还包括各种标点符号、连字符等。因此,strings.Title 可能无法正确处理这些情况。golang.org/x/text/cases 包提供了更准确的 Unicode 处理方式。

cases.Title()strings.Title()的区别

让我们直接通过一些简单的示例来对比这两个函数的输出差异。

package main

import (
	"fmt"
	"strings"

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

func main() {
	// 示例字符串
	str1 := "hello.world"
	str2 := "HELLO world"
	str3 := "heLlO wOrLD"
	c := cases.Title(language.English)

	fmt.Printf("%s: %s vs %s\n", str1, strings.Title(str1), c.String(str1))
	fmt.Printf("%s: %s vs %s\n", str2, strings.Title(str2), c.String(str2))
	fmt.Printf("%s: %s vs %s\n", str3, strings.Title(str3), c.String(str3))

}

输出结果:

hello.world: Hello.World vs Hello.world
HELLO world: HELLO World vs Hello World
heLlO wOrLD: HeLlO WOrLD vs Hello World

以上是英语环境下的输出差异,接下来看看不同语言(英语、荷兰语)的输出差异:

package main

import (
	"fmt"
	"strings"

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

func main() {
	src := []string{
		"'n ijsberg",
		"here comes A'Xiaoxin",
	}

	// Deprecated: strings.Title
	fmt.Println("strings.Title:")
	for _, s := range src {
		fmt.Println(strings.Title(s))
	}

	// cases.Title with different language
	for _, tag := range []language.Tag{language.English, language.Dutch} {
		caser := cases.Title(tag)
		fmt.Println()
		fmt.Println("Using cases.Title with caser:", display.Self.Name(tag))
		for _, s := range src {
			fmt.Println(caser.String(s))
		}
	}
}

输出结果:

strings.Title:
'N Ijsberg
Here Comes A'Xiaoxin

Using cases.Title with caser: English
'N Ijsberg
Here Comes A'xiaoxin

Using cases.Title with caser: Nederlands
'n IJsberg
Here Comes A'xiaoxin

你应该发现区别了吧!使用 cases.Title 得到的结果才是真正符合具体语言的标题化输出。

既然strings.Title()已被废弃,我们就更新代码,使用cases.Title()来代替吧。

cases.Title详细用法介绍

cases.Title 的主要功能:

  • 语言特定的标题大小写转换:根据指定语言的规则,将字符串转换为标题大小写。
  • 准确处理 Unicode 标点符号:遵循 Unicode 标准的单词边界定义,处理各种标点符号和特殊字符。

cases.Title 使用了 transform.Transformer 接口,这意味着它可以处理字节切片和字符串的转换。它内部依赖于 Unicode 标准定义的单词边界和大小写规则,能够正确处理各种语言的特殊情况和标点符号。

以下是 cases.Title 的详细用法说明和示例代码:

要使用 cases.Title,你需要导入 golang.org/x/text/casesgolang.org/x/text/language 包。

  • 创建 Caser:使用 cases.Title 创建一个新的 Caser,可以指定语言和选项。
  • 转换字符串:使用 Caser.String 方法,将字符串转换为指定的标题大小写形式。
  • 转换字节切片:使用 Caser.Bytes 方法,将字节切片转换为指定的标题大小写形式。
  • 重置状态:使用 Caser.Reset 方法,重置 Caser 以便再次使用。

示例代码

package main

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",
	}

	// 创建不同语言和选项的 Caser
	casers := []cases.Caser{
		cases.Lower(language.Und),             // 无特定语言的全小写转换
		cases.Upper(language.Turkish),         // 土耳其语特定的大写转换
		cases.Title(language.Dutch),           // 荷兰语特定的标题转换
		cases.Title(language.Und, cases.NoLower), // 默认标题转换,不进行小写处理
	}

	// 输出转换结果
	for _, caser := range casers {
		fmt.Println()
		fmt.Printf("使用 %T (%v) 进行转换:\n", caser, caser)
		for _, s := range src {
			fmt.Println(caser.String(s))
		}
	}
}

示例解释

  • cases.Lower(language.Und):将字符串转换为小写,language.Und 表示不特定任何语言。
  • cases.Upper(language.Turkish):根据土耳其语的规则,将字符串转换为大写。例如,土耳其语中的小写 i 转换为大写 İ
  • cases.Title(language.Dutch):根据荷兰语的规则,将字符串转换为标题大小写。
  • cases.Title(language.Und, cases.NoLower):默认标题转换,但不将非首字母转换为小写。

完整示例输出:

使用 cases.Caser ({0x5e2d60}) 进行转换:
hello world!
i with dot
'n ijsberg
here comes o'brian

使用 cases.Caser ({0xc000090000}) 进行转换:
HELLO WORLD!
İ WİTH DOT
'N İJSBERG
HERE COMES O'BRİAN

使用 cases.Caser ({0xc00008a0a0}) 进行转换:
Hello World!
I With Dot
'n IJsberg
Here Comes O'brian

使用 cases.Caser ({0xc00008a140}) 进行转换:
Hello World!
I With Dot
'N Ijsberg
Here Comes O'Brian

从输出可以看到,cases.Title 能够准确处理带有 Unicode 标点符号的字符串,并根据不同语言的规则进行标题大小写转换,比 strings.Title 更加健壮和准确。

相关阅读:如何在 Golang 中将语言代码字符串转换为 IETF 标准的 language.Tag

结论

尽管strings.Title()在许多简单场景下仍然可以使用,但对于需要处理多语言和 Unicode 字符的应用,cases.Title()无疑是更好的选择。通过了解和使用这个新方法,我们可以编写出更加健壮和国际化的 Go 代码。

希望这篇文章能帮助你理解为什么strings.Title()被废弃,以及如何使用cases.Title()来替代它。如果你在使用过程中有任何问题,欢迎在评论区留言讨论。


也可以看看