先上讨论的题目

1
2
3
4
5
6
fmt.Println(strings.TrimSpace(" foot   "), "end")
fmt.Println(strings.Trim("fo offe ", "fo"))
fmt.Println(strings.TrimLeft("foo", "fo"))
fmt.Println(strings.TrimRight("foo", "fo"))
fmt.Println(strings.TrimPrefix("afoo", "fo"))
fmt.Println(strings.TrimSuffix("foo", "fo"))

输出

1
2
3
4
5
6
foot end
 offe 


afoo
foo

这道题很简单,只不过它的处理方式让我难以接受。先看Trim:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
//TrimRightFunc和TrimLeftFunc
func Trim(s string, cutset string) string {
	if s == "" || cutset == "" {
		return s
	}
	return TrimFunc(s, makeCutsetFunc(cutset))
}

func TrimFunc(s string, f func(rune) bool) string {
	return TrimRightFunc(TrimLeftFunc(s, f), f)
}

func makeCutsetFunc(cutset string) func(rune) bool {
	if len(cutset) == 1 && cutset[0] < utf8.RuneSelf {
		return func(r rune) bool {
			return r == rune(cutset[0])
		}
	}
	if as, isASCII := makeASCIISet(cutset); isASCII {
		return func(r rune) bool {
			return r < utf8.RuneSelf && as.contains(byte(r))
		}
	}
    // 判断r是否在cutset中
	return func(r rune) bool { return IndexRune(cutset, r) >= 0 } 
}

//从右开始删除在cutset中的rune,直到碰见不在cutset的rune字符
func TrimRightFunc(s string, f func(rune) bool) string {
	i := lastIndexFunc(s, f, false)
	if i >= 0 && s[i] >= utf8.RuneSelf {
		_, wid := utf8.DecodeRuneInString(s[i:])
		i += wid
	} else {
		i++
	}
	return s[0:i]
}

//从左开始删除在cutset中的rune,直到碰见不在cutset的rune字符
func TrimLeftFunc(s string, f func(rune) bool) string {
	i := indexFunc(s, f, false)
	if i == -1 {
		return ""
	}
	return s[i:]
}

//truth为false,此函数在此功能的意义是s从左到右找出第一个不在cutset中的rune字符的位置
func indexFunc(s string, f func(rune) bool, truth bool) int {
	for i, r := range s {
		if f(r) == truth {
			return i
		}
	}
	return -1
}

//truth为false,此函数在此功能的意义是s从右到左找出第一个不在cutset中的rune字符的位置
func lastIndexFunc(s string, f func(rune) bool, truth bool) int {
	for i := len(s); i > 0; {
		r, size := utf8.DecodeLastRuneInString(s[0:i]) //取出rune字符
		i -= size 
		if f(r) == truth {
			return i
		}
	}
	return -1
}

这就是为什么第三、四行都输出空字符串的原因。而第五、六行的TrimPrefixTrimSuffix跟前面的四个有本质区别,它们为一类。我们拿TrimPrefix分析:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
//去掉前缀
func TrimPrefix(s, prefix string) string {
	if HasPrefix(s, prefix) {
		return s[len(prefix):]
	}
	return s
}
//整个prefix作为一个整体比较而不是单个rune,而且只作用一次
func HasPrefix(s, prefix string) bool {
	return len(s) >= len(prefix) && s[0:len(prefix)] == prefix
}

所以fmt.Println(strings.TrimPrefix("afoo", "fo"))fo不是afoo前缀,故不做处理。

参考

https://yourbasic.org/golang/string-functions-reference-cheat-sheet/