我有一个函数可以从一串有效字符中生成一个随机字符串。当它选择一个 £ 时,我偶尔会得到奇怪的结果
我已将其复制到以下最小示例中:
func foo() string {
validChars := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789~@:!£$%^&*"
var result strings.Builder
for i := 0; i < len(validChars); i++ {
currChar := validChars[i]
result.WriteString(string(currChar))
}
return result.String()
}
Run Code Online (Sandbox Code Playgroud)
我希望这会回来
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789~@:!£$%^&*
但它没有,它产生
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789~@:!£$%^&*
^
where did you come from ?
Run Code Online (Sandbox Code Playgroud)
如果我从原始的 validChars 字符串中取出 £ 符号,那个奇怪的 A 就会消失。
func foo() string {
validChars := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789~@:!$%^&*"
var result strings.Builder
for i := 0; i < len(validChars); i++ {
currChar := validChars[i]
result.WriteString(string(currChar))
}
return result.String()
}
Run Code Online (Sandbox Code Playgroud)
这产生
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789~@:!$%^&*
Astring是 的类型别名[]byte。你对 a 的心智模型string可能是它由一片字符组成——或者,正如我们在 Go 中所说的那样:一片rune.
对于validChars字符串中的许多符文,这很好,因为它们是 ASCII 字符的一部分,因此可以在 UTF-8 中以单个字节表示。但是,£符文表示为 2 个字节。
现在,如果我们考虑一个 string £,它由 1 个符文但 2 个字节组成。正如我所提到的,一个字符串实际上只是一个[]byte. 如果我们像您在示例中有效地那样获取第一个元素,我们将只获取代表£. 当你将它转换回字符串时,它会给你一个意想不到的符文。
解决您的问题的方法是首先将字符串转换validChars为[]rune. 然后,您可以通过索引访问其各个符文(而不是字节),并且foo将按预期工作。您可以在这个 Playground 中看到它的运行情况。
另请注意,这len(validChars)将为您提供字符串中的字节数。要获得符文的数量,请utf8.RuneCountInString改用。
最后,这里是 Rob Pike 发表的一篇关于您可能会感兴趣的主题的博客文章。