golang,£ char 导致奇怪的字符

Stu*_*art 2 go

我有一个函数可以从一串有效字符中生成一个随机字符串。当它选择一个 £ 时,我偶尔会得到奇怪的结果

我已将其复制到以下最小示例中:

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~@:!$%^&*

Emi*_*els 9

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 发表的一篇关于您可能会感兴趣的主题的博客文章。