Golang中strings.Trim()方法的怪异行为解析

Golang中strings.Trim()方法的怪异行为解析 各位Gopher们,我在使用以下代码时遇到了奇怪的结果:

prefix := "/opt/research"
l := []string{
	"/opt/research/comm/aa/bb/file1.xml",
	"/opt/research/comm/cc/dd/file2.xml",
	"/opt/research/non_comm/aa/bb/file1.xml",
	"/opt/research/non_comm/cc/dd/file2.xml",
}

for _, v := range l {
	fmt.Println(strings.TrimLeft(v, prefix))
}

(代码演示链接:https://play.golang.org/p/QhWN-MUk97Q)

我期望得到这样的输出:

comm/aa/bb/file1.xml
comm/cc/dd/file2.xml
non_comm/aa/bb/file1.xml
non_comm/cc/dd/file2.xml

但实际上我得到了这样的结果:

mm/aa/bb/file1.xml
mm/cc/dd/file2.xml
non_comm/aa/bb/file1.xml
non_comm/cc/dd/file2.xml

使用strings.Trim()也会出现同样的问题。

有人知道这是为什么吗?


更多关于Golang中strings.Trim()方法的怪异行为解析的实战教程也可以访问 https://www.itying.com/category-94-b0.html

3 回复

我认为TrimPrefix是最佳选择。

更多关于Golang中strings.Trim()方法的怪异行为解析的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


strings.TrimLeft的第二个参数名为cutset,而不是前缀。因此它会从左侧删除所有在cutset中的字符。

正如文档所述,同时文档也证实了以下说法:

要删除前缀,请改用TrimPrefix。

strings.TrimLeft 的行为与你的预期不符,是因为它并不是按前缀字符串来移除,而是将第二个参数视为一个字符集合(字符集),然后从字符串开头逐个移除所有出现在该字符集中的字符。

在你的例子中,prefix"/opt/research",它会被当作字符集 {'/', 'o', 'p', 't', '/', 'r', 'e', 's', 'e', 'a', 'r', 'c', 'h'} 处理。
当处理第一个路径 /opt/research/comm/aa/bb/file1.xml 时,strings.TrimLeft 会从开头依次移除所有出现在字符集中的字符,直到遇到第一个不在字符集中的字符为止。

我们来看第一个路径的移除过程:

  • 开头 / 在字符集中 → 移除
  • o 在字符集中 → 移除
  • p 在字符集中 → 移除
  • t 在字符集中 → 移除
  • / 在字符集中 → 移除
  • r 在字符集中 → 移除
  • e 在字符集中 → 移除
  • s 在字符集中 → 移除
  • e 在字符集中 → 移除
  • a 在字符集中 → 移除
  • r 在字符集中 → 移除
  • c 在字符集中 → 移除
  • h 在字符集中 → 移除
  • 接下来是 /comm/aa/bb/file1.xml 吗?不对,因为 h 移除后,下一个字符是 /(在字符集中)也会被移除,然后才是 c(在字符集中)被移除,再下一个是 o(在字符集中)被移除,再下一个是 m(不在字符集中)停止。

所以最终剩下的是 "mm/aa/bb/file1.xml"


正确做法
如果你想去掉固定的前缀字符串,应该用 strings.TrimPrefix 而不是 strings.TrimLeft

示例代码:

prefix := "/opt/research"
l := []string{
	"/opt/research/comm/aa/bb/file1.xml",
	"/opt/research/comm/cc/dd/file2.xml",
	"/opt/research/non_comm/aa/bb/file1.xml",
	"/opt/research/non_comm/cc/dd/file2.xml",
}

for _, v := range l {
	fmt.Println(strings.TrimPrefix(v, prefix))
}

输出:

/comm/aa/bb/file1.xml
/comm/cc/dd/file2.xml
/non_comm/aa/bb/file1.xml
/non_comm/cc/dd/file2.xml

如果你不想要开头的 /,可以再处理一下:

for _, v := range l {
	s := strings.TrimPrefix(v, prefix)
	if len(s) > 0 && s[0] == '/' {
		s = s[1:]
	}
	fmt.Println(s)
}

这样输出就是你期望的:

comm/aa/bb/file1.xml
comm/cc/dd/file2.xml
non_comm/aa/bb/file1.xml
non_comm/cc/dd/file2.xml
回到顶部