Go 语言中的字符串忽略大小写的相等性判断 与 Unicode Case-folding

陪她去流浪 桃子 2020年07月24日 编辑 阅读次数:4409

对于绝大多数刚学习 Go 语言不久的人来说,如果你问他应该如何忽略大小写判断两个字符串相等,他大有可能写出类似下面这样的代码:

1
2
a, b := "Abc", "abc"
fmt.Println(strings.ToLower(a) == strings.ToLower(b))

或者是用 strings.ToUpper(),再或者是 strings.ToTitle()。 这些都是大家的习惯做法,这种写法也没有错误。

但是有没有人会好奇:为什么 Go 语言没有直接提供一个忽略大小写的函数? 先问是不是,再问为什么。 Go 其实是有提供的,只是一开始,很少有人知道罢了。就算后来知道了,可能又忘记名字了。

这个函数就是:func EqualFold(s, t string) bool

1
fmt.Println(strings.EqualFold("Go", "go")) // true

Go 标准库官方解释:

EqualFold reports whether s and t, interpreted as UTF-8 strings, are equal under Unicode case-folding, which is a more general form of case-insensitivity.

EqualFold 基于 Unicode case-folding 判断两个 UTF-8 字符串是否相等。这是 大小写不敏感比较 的更通用形式。

题外话,Elasticsearch 中也有类似的名词。见:ASCII folding token filter

所以 Fold 是个什么鬼?

Fold 全称 Case Folding,暂时不知道中文叫什么翻译,大小写折叠?其来源于:Unicode 规范化(Unicode Normalization)。 Case-folding 的提出是为了解决国际语言的大小写问题,而不止是英语(拉丁系,英语中也有少部分外来词是这样)。

比如“简历”这个单词 résumé 的大写,如果只是简单地把 [a-z] 映射成 [A-Z],得到的结果是:RéSUMé。 而实际上,这个单词的大写应该是:RÉSUMÉ

为什么 Go 语言引入如此专业的名词,而不直接用类似 "EqualNoCase()" 之类的更加通俗易懂的词语? 我不知道,大概是想让更多的人知道“标准”的存在,能更多地知道制定标准的人所作的工作。

学了 Go 语言之后再回去看 C 语言的字符串忽略大小写的比较:

1
2
if (strcmp(toupper(foo),toupper(bar))==0) { // a typical caseless comparison
// 或 strcasecmp 或 stricmp 之类

以上写法根本无法应对非拉丁语系语言。 C 语言里面的“字符”在现代看来,应该说是“字节”才正确。 Go 语言里面的 rune 才能算作是一个完整的 Unicode 字符(码点)。

标签:Go · Unicode