这是一篇译文,原文地址:https://www.calhoun.io/6-tips-for-using-strings-in-go/
如果你是从其他语言(比如 Ruby 或者 Python) 转型到 Go,那么需要做很多的转变。比如处理 string 类型。以下是一些字符串的技巧,这些技巧解决了我最初几周在使用 Go 时遇到的问题。
在 Go 中创建一个多行字符串很简单。只需要在你声明或者赋值时使用 (``)。
str := `This is amultilinestring.`
请注意-任何在字符串中的缩进都将保留在最终的结果。
str := `This stringwill have tabs in it`
Go 允许你通过 "+" 的方式连接字符串,但这种方式在处理大量字符串连接时会非常低效。
使用 bytes.Buffer 连接字符串是一个更高效的方式,它会一次将所有的内容连接起来转换成字符串。
package mainimport ("bytes""fmt")func main() { var b bytes.Bufferfor i := 0; i < 1000; i++ { b.WriteString(randString()) } fmt.Println(b.String())}func randString() string { // 模拟返回随机字符串return "abc-123-"}
如果你提前准备好所有的字符串,你也可以使用 strings.Join 的方式实现。
package mainimport ("fmt""strings")func main() { var strs []stringfor i := 0; i < 1000; i++ { strs = append(strs, randString()) } fmt.Println(strings.Join(strs, ""))}func randString() string {return "abc-123-"}
作者也提到在Go 1.10 时引入了 Builder 类型,它更容易使用,而且通常更加高效。那么我们也把这个加上。
package mainimport ("fmt""strings")func main() {var builder strings.Builderfor i := 0; i < 10; i++ { builder.WriteString(randString()) } fmt.Println(builder.String())}func randString() string {return "abc-123-"}
我还知道有一种,使用 []byte。(译者注)
package mainimport "fmt"func main() { buf := make([]byte, 0)for i := 0; i < 10; i++ { buf = append(buf, randString()...) } fmt.Println(string(buf))}func randString() string {return "abc-123-"}
在大多语言中,可以轻易地把任意数据类型转换为字符串进行拼接,或者使用字符串插值将其转换成字符串(例如在 ruby 中 “ID=#{id}”)。不幸的是,如果你尝试在 Go 中做这种明显的操作,比如强制将 int 转换成 string,那么你不会得到期望的值。
i := 123s := string(i)
你希望 s 的输出是多少?如果你像大多数人猜测是 "123",那么你就错了。
你应该使用 strconv 这样的包或者像 fmt.Sprintf 这样的函数进行转换。下面是一个使用 strconv.Itoa 将整数转换成字符串的示例。
package mainimport ( "fmt" "strconv")func main() { i := 123 t := strconv.Itoa(i) fmt.Println(t)}
你还可以使用 fmt.Sprintf 函数将几乎所有数据类型转换为字符串。但这通常保留给实际创建带有嵌入数据的字符串实例,而不是在希望将单个整数转换为字符串时。
package mainimport "fmt"func main() {i := 123 t := fmt.Sprintf("We are currently processing ticket number %d.", i) fmt.Println(t)}
fmt.Sprintf 的操作和 fmt.Printf 几乎相同,只是它没有将结果字符串打印到标准输出,而是将其作为字符串返回。
限制使用 fmt.Sprintf。fmt.Sprintf 通常应用在创建具有嵌入值的字符串。这有几个问题,最突出的是 fmt.Sprintf 不做任何类型检查,因此在实际运行代码之前,不太可能发现这个问题。fmt.Sprintf 通常比你使用 strconv 包中的大多数函数慢,不过这种速度差异很小,一般不值得讨论。
这并不是一个真正的【快速技巧】,但我发现这是一个经常被问到的问题。
如何在 Go 中创建随机的字符串?
听上去很简单,许多语言,比如 Ruby 和 Python,都提供了一些帮助程序使字符串的生成变得非常简单,所以 Go 肯定也有这样的工具,对吧?答案是错的。
Go 选择只提供创建随机字符串的工具,而将细节留给开发人员。虽然一开始觉得麻烦,但是好处是自己可以完全决定如何生成字符串。这意味着你可以指定字符集、随机生成的方式以及生成的细节。简而言之,你有了更多的控制权,但代价是需要编写一些额外的代码。
下面是一个使用 math/rand 包和一组以字母数字字符作为字符集的快速示例。
package mainimport ("fmt""math/rand""time")func main() { fmt.Println(RandString(10))}var source = rand.NewSource(time.Now().UnixNano())const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"func RandString(length int) string { b := make([]byte, length)for i := range b { b[i] = charset[source.Int63()%int64(len(charset))] }return string(b)}
对于一些特定的需求,可能有比这个更优的解决方案,但这是一个好的起点。如果你正在寻找改进代码的方法,你可以考虑使用 crypto/rand 包来生成随机数据-这通常更安全,但最终可能需要更多的工作。
对于大多数不涉及密码和身份验证等敏感数据的应用,它足够使用了。只是一定要记住你的随机数发生器!这可以通过 math/rand 包中的 rand.Seed 函数完成,或者创建一个源数据。在上面的例子中,我选择创建一个源数据。
在处理字符串时,想要知道一个字符串是以一个特定的字符串开始或者是以一个特定的字符串结束是非常常见的情况。
例如,如果你的 API 键都是以 sk_ 开头,那么你可能希望验证 API 请求中提供的所有API 键都以这个前缀开头。
对于一些常用的操作,最好的选择通常是去 strings 包检查一遍可能会有帮助的东西。基于上面的需求,可以使用 strings.HasPrefix(str,prefix) 以及 strings.HasSuffix(str,prefix),看看他们的使用。
package mainimport ("fmt""strings")func main() { fmt.Println(strings.HasPrefix("something", "some")) fmt.Println(strings.HasSuffix("something", "thing"))}
虽然 strings 包中有许多有用的通用函数,但并不总是值得去寻找一个能够满足你需求的包。我经常看到开发人员花了太多时间寻找他们需要的包,其实他们本来可以很容易地自己编写代码。
使用标准库肯定有一定的好处:他们经过了彻底的测试,并拥有良好的文档记录。尽管有这些好处,但如果你发现自己花了很多时间寻找一个函数,那么自己编写通常有同样的好处。在这种情况下,你会根据自己的需求进行定制化。你会完全理解发生了什么,而不会被一些奇怪的边界情况弄的措手不及。
Go 语言可以将一个字符串转换成 byte 切片(byte[]),也可以将 byte[] 转换成字符串。执行这种转换通常是为了将字符串传递给接收 byte[] 的函数,或者将 byte[] 传递给需要字符串的函数。
下面是一个转换的例子。
package mainimport "fmt"func main() {var s string = "this is a string" fmt.Println(s)var b []byte b = []byte(s) fmt.Println(b)for i := range b { fmt.Println(string(b[i])) } s = string(b) fmt.Println(s)}
以上就是 Go 语言字符串使用过程中的一些小技巧,希望能帮到你。
如果你还有其他的技巧,欢迎留言?
如果文章对你有所帮助,点赞、转发、留言都是一种支持!