分享6个Go处理字符串的技巧小结

蔚蓝色的天,深蓝色的海,金黄色的沙,灰白色的鸥,穿着米白格子衬衫的你,站在金灿灿的阳光下,很耀眼。

如果你从 Ruby 或者 Python 转型到 Go,将会有很多语言差异需要学习,其中很多问题都是围绕处理 string 类型。

下面是一些字符串的技巧,这些技巧解决了我在使用 Golang 的最初几周中遇到的问题。

1. 多行字符串

在 Go 中创建多行字符串非常容易。只需要在你声明或赋值时使用 (``) 。

str := `This is a
multiline
string.`

注意 - 任何你在字符串中的缩进都将被保留在最终结果。

str := `This string
  will have
  tabs in it`

2. 高效的字符串连接方式

Go 允许你通过 "+" 的方式连接字符串,但这种方式在处理大量字符串连接的场景下将非常低效。使用 bytes.Buffer 连接字符串是一个更高效的方式,它会一次性将所有的内容连接起来转化成字符串。

package main

import (
  "bytes"
  "fmt"
)

func main() {
  var b bytes.Buffer

  for i := 0; i < 1000; i++ {
    b.WriteString(randString())
  }

  fmt.Println(b.String())
}

func randString() string {
  // 模拟返回一个随机字符串
  return "abc-123-"
}

如果你提前准备好所有字符串,你也可以通过 strings.Join 的方式实现。

package main

import (
  "fmt"
  "strings"
)

func main() {
  var strs []string

  for i := 0; i < 1000; i++ {
    strs = append(strs, randString())
  }

  fmt.Println(strings.Join(strs, ""))
}

func randString() string {
  // 模拟返回一个随机字符串
  return "abc-123-"
}

3. 将整型 (或任意数据类型) 转为字符串

在大多数语言中,可轻易地将任意数据类型转型为字符串进行拼接,或用字符串插入(例如在 ruby 中这样 "ID=#{id}")。很不幸,如果你尝试在 Go 中去做这种显示而易见的操作,比如强制将整形转为字符串,你不会得到期望的结果。

i := 123
s := string(i)

你希望 s 的输出是多少?如果你像大多数人一样猜测 "123",那你就大错特错了。相反,你会得到类似 "E" 的值。这根本不是我们想要的!

相反,您应该使用像 [strconv] (https://golang.org/pkg/strconv/) 这样的包或像 fmt.Sprintf 这样的函数。例如,下面是一个使用 strconv.Itoa 将整数转换为字符串的示例。

package main

import (
  "fmt"
  "strconv"
)

func main() {
  i := 123
  t := strconv.Itoa(i)
  fmt.Println(t)
}

你还可以使用 fmt.Sprintf 函数将几乎所有数据类型转换为字符串,但通常应保留在这样的实例上,如正在创建的字符串包含嵌入数据,而非在期望将单个整数转换为字符串时用。

package main

import "fmt"

func main() {
  i := 123
  t := fmt.Sprintf("We are currently processing ticket number %d.", i)
  fmt.Println(t)
}

Sprintf 的操作与 fmt.Printf 几乎相同,只是它没有将结果字符串输出到标准输出,而是将其作为字符串返回。

限制使用 Sprintf

如前所述,fmt.Sprintf 通常应用在创建具有嵌入值的字符串。这有几个原因,但最突出的一个原因是 fmt.Sprintf 不做任何类型检查,因此在实际运行代码之前,您不太可能发现任何错误。

Sprintf 也比你通常在 strconv 包中使用的大多数函数慢,不过如果我说实话,速度差异是如此之小,一般不值得考虑。

4. 创建随机字符串

这并不是一个真正的「快速技巧」,但我发现这是一个经常被问到的问题。

如何在 Go 中创建随机的字符串?

听上去很简单。许多语言,比如 Ruby 和 Python,都提供了一些帮助程序,使随机字符串的生成变得非常简单,所以 Go 肯定有这样一个工具,对吧?答案是错误的。
Go 选择只提供创建随机字符串的工具,而将细节留给开发人员。虽然一开始可能会有些困难,但好处是您可以完全决定如何生成字符串。 这意味着您可以指定字符集、如何播种随机生成以及任何其他详细信息。简而言之,你有更多的控制权,但代价是需要写一些额外的代码。

下面是一个使用 math/rand 包和一组字母数字字符作为字符集的快速示例。

package main

import (
  "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)
}

Go 练习场总是输出相同的字符串

如果您在 Go 练习场上多次运行这段代码,您可能会注意到它总是输出相同的字符串 - aJFLa7XPH5。

这是因为 Go 练习场总是使用相同的时间,所以当我们用 rand.NewSource 方法时。在当前时间内传递的那个值总是相同的,所以我们生成的字符串总是相同的。

对于您的特定需求,可能有比这个更优的解决方案,但这是一个很好的起点。如果您正在寻找改进 / 更改代码的方法,您可能会考虑使用 crypto/rand 包来生成随机数据 — 这通常更安全,但最终可能需要更多的工作。

无论您最终使用的是什么,这个示例都应该有助于您入门。 对于大多数不涉及密码和身份验证系统等敏感数据的实际用例,它工作得足够好。 只是一定要记得你的种子随机数发生器!这可以通过 rand.Seed 函数在 math/rand 包中完成,或者创建一个源代码。在上面的例子中,我选择创建一个源代码。

5. strings 包、HasPrefix 和自定义代码

在处理字符串时,想要知道一个字符串是以一个特定的字符串开始还是以一个特定的字符串结束是非常常见的情况。例如,如果您的 API 键都以 sk_ 开头,那么您可能希望验证 API 请求中提供的所有 API 键都以这个前缀开头,否则进行数据库查找将浪费大量时间。

对于那些听起来像是非常常见的用例的函数,您最好的选择通常是直接访问 strings 包并检查一些可能对您有帮助的内容。在这种情况下,你会想要使用功能HasPrefix(str, prefix) 和strings.HasSuffix(str, prefix) 。 你可以在下面看到他们的用法。

package main

import (
  "fmt"
  "strings"
)

func main() {
  fmt.Println(strings.HasPrefix("something", "some"))
  fmt.Println(strings.HasSuffix("something", "thing"))
}

虽然 strings 包中有大量有用的公共函数,但值得注意的是,并不总是值得去寻找一个能满足您需要的包。如果你有其他语言经验正在学习 Go 语言,一个常见的错误是开发者花太多时间寻找能够提供所需功能的包,而他们自己可轻易地编码实现这功能。

使用标准库肯定有好处(如它们经过了彻底的测试并有很好的文档记录)。尽管有这些好处,但如果你发现自己花了超过几分钟的时间来寻找一个函数,那么自己编写它通常也是有益的。在这种情况下,根据需求自定义(编码),将很快完成,你将完全了解正在发生的事情,不会被奇怪的边界情况(译者注如索引越界)措手不及。您也不必担心其他人维护代码。

6. 字符串可以被转换成 byte 切片 (反之亦然)

Go 语言可以将一个字符串转换成 byte 切片 ([]byte) ,也可以将 byte 切片转换成字符串。转换的过程跟其他任意类型转换的方式一样简单。这种转换方式通常用于为一个接收 byte 切片参数的函数传递一个字符串 以及 为一个接收字符串参数的函数传递 byte 切片的场景。

下面是一个转换的例子:

package main

import "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 语言字符串使用过程中的一些小技巧,希望能帮到你。如果你需要更多 Go 相关的实践,可以查阅我发表的其他相关好代码教程。

原文地址:https://www.calhoun.io/6-tips-for-using-strings-in-go/