• Go语言 String 简介


    Go 中的字符串值得特别提及,因为与其他语言相比,它们在实现上有所不同。

    什么是字符串?

    字符串是Go 中的一段字节。可以通过将一组字符括在双引号内来创建字符串" "

    Go 中的字符串符合 Unicode采用 UTF-8 编码

    让我们看一个创建 string并打印它的简单示例。

    package main
    
    import (  
        "fmt"
    )
    
    func main() {  
        name := "Hello World"
        fmt.Println(name)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    Run in playground

    上面的程序将打印Hello World.

    访问String的各个字节

    由于字符串是字节切片,因此可以访问字符串的每个字节。

    package main
    
    import (  
        "fmt"
    )
    
    func printBytes(s string) {  
        fmt.Printf("Bytes: ")
        for i := 0; i < len(s); i++ {
            fmt.Printf("%x ", s[i])
        }
    }
    
    func main() {  
        name := "Hello World"
        fmt.Printf("String: %s\n", name)
        printBytes(name)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    Run in playground

    %s 是打印字符串的格式说明符。在上面的程序中,len(s) 返回字符串中的字节数,我们使用for 循环以十六进制表示法打印这些字节。

    **%x 是十六进制的格式说明符。**上述程序输出

    String: Hello World  
    Bytes: 48 65 6c 6c 6f 20 57 6f 72 6c 64  
    
    • 1
    • 2

    这些是的[Unicode UT8 编码](https://mothereff.in/utf-8#Hello World)值Hello World

    为了更好地理解字符串,需要对 Unicode 和 UTF-8 有基本的了解。

    我建议阅读https://naveenr.net/unicode-character-set-and-utf-8-utf-16-utf-32-encoding/以了解有关 Unicode 和 UTF-8 的更多信息。

    访问字符串的各个字符

    让我们稍微修改一下上面的程序来打印字符串的字符。

    package main
    
    import (  
        "fmt"
    )
    
    func printBytes(s string) {  
        fmt.Printf("Bytes: ")
        for i := 0; i < len(s); i++ {
            fmt.Printf("%x ", s[i])
        }
    }
    
    func printChars(s string) {  
        fmt.Printf("Characters: ")
        for i := 0; i < len(s); i++ {
            fmt.Printf("%c ", s[i])
        }
    }
    
    func main() {  
        name := "Hello World"
        fmt.Printf("String: %s\n", name)
        printChars(name)
        fmt.Printf("\n")
        printBytes(name)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    Run in playground

    在上面程序中,%c格式说明符用于printChars方法中打印字符串的字符。程序打印出

    String: Hello World  
    Characters: H e l l o   W o r l d  
    Bytes: 48 65 6c 6c 6f 20 57 6f 72 6c 64  
    
    • 1
    • 2
    • 3

    尽管上面的程序看起来是访问字符串各个字符的合法方法,但这有一个严重的错误。让我们找出这个错误是什么。

    package main
    
    import (  
        "fmt"
    )
    
    func printBytes(s string) {  
        fmt.Printf("Bytes: ")
        for i := 0; i < len(s); i++ {
            fmt.Printf("%x ", s[i])
        }
    }
    
    func printChars(s string) {  
        fmt.Printf("Characters: ")
        for i := 0; i < len(s); i++ {
            fmt.Printf("%c ", s[i])
        }
    }
    
    func main() {  
        name := "Hello World"
        fmt.Printf("String: %s\n", name)
        printChars(name)
        fmt.Printf("\n")
        printBytes(name)
        fmt.Printf("\n\n")
        name = "Señor"
        fmt.Printf("String: %s\n", name)
        printChars(name)
        fmt.Printf("\n")
        printBytes(name)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    Run in playground

    上述程序的输出是

    String: Hello World  
    Characters: H e l l o   W o r l d  
    Bytes: 48 65 6c 6c 6f 20 57 6f 72 6c 64 
    
    String: Señor  
    Characters: S e à ± o r  
    Bytes: 53 65 c3 b1 6f 72  
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    在上面程序的第 30 行中,我们尝试打印 Señor 的字符,它输出 S e à ± o r,这是错误的。为什么这个程序在它工作得很好时会中断.原因是 的 Unicode 码位及其 UTF-8 编码占用 2 个字节我们尝试打印字符,假设每个代码点将是一个字节长,这是错误的。

    在 UTF-8 编码中,一个码位可以占用 1 个以上的字节。那么我们如何解决这个问题呢?这就是Rune拯救我们的地方。

    Rune

    rune 是 Go 中的内置类型,它是 int32 的别名。Rune 代表 Go 中的 Unicode 代码点。不管代码点占用多少字节,它都可以用Rune来表示。让我们修改上面的程序以使用Rune打印字符。

    package main
    
    import (  
        "fmt"
    )
    
    func printBytes(s string) {  
        fmt.Printf("Bytes: ")
        for i := 0; i < len(s); i++ {
            fmt.Printf("%x ", s[i])
        }
    }
    
    func printChars(s string) {  
        fmt.Printf("Characters: ")
        runes := []rune(s)
        for i := 0; i < len(runes); i++ {
            fmt.Printf("%c ", runes[i])
        }
    }
    
    func main() {  
        name := "Hello World"
        fmt.Printf("String: %s\n", name)
        printChars(name)
        fmt.Printf("\n")
        printBytes(name)
        fmt.Printf("\n\n")
        name = "Señor"
        fmt.Printf("String: %s\n", name)
        printChars(name)
        fmt.Printf("\n")
        printBytes(name)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    Run in playground

    上面程序中,字符串被转换为Rune切片。然后我们循环它并显示字符。该程序打印,

    String: Hello World  
    Characters: H e l l o   W o r l d  
    Bytes: 48 65 6c 6c 6f 20 57 6f 72 6c 64 
    
    String: Señor  
    Characters: S e ñ o r  
    Bytes: 53 65 c3 b1 6f 72  
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    上面的输出是完美的。只是想要我们想要的😀。

    使用 for range 循环访问单个Rune

    上面的程序是迭代字符串的各个Rune的完美方法。但是 Go 为我们提供了一种更简单的方法来使用for range循环来做到这一点。

    package main
    
    import (  
        "fmt"
    )
    
    func charsAndBytePosition(s string) {  
        for index, rune := range s {
            fmt.Printf("%c starts at byte %d\n", rune, index)
        }
    }
    
    func main() {  
        name := "Señor"
        charsAndBytePosition(name)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    Run in playground

    在上面程序的第 8 行中,使用for range循环迭代字符串。该程序输出

    S starts at byte 0  
    e starts at byte 1  
    ñ starts at byte 2
    o starts at byte 4  
    r starts at byte 5  
    
    • 1
    • 2
    • 3
    • 4
    • 5

    从上面的输出可以清楚地看出,它ñ占用 2 个字节,因为下一个字符o从字节 4 开始,而不是字节 3 😀。

    从字节切片创建字符串

    package main
    
    import (  
        "fmt"
    )
    
    func main() {  
        byteSlice := []byte{0x43, 0x61, 0x66, 0xC3, 0xA9}
        str := string(byteSlice)
        fmt.Println(str)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    Run in playground

    程序打印出

    Café  
    
    • 1

    如果我们是十进制怎么办?上面的程序能运行吗?让我们来看看。

    package main
    
    import (  
        "fmt"
    )
    
    func main() {  
        byteSlice := []byte{67, 97, 102, 195, 169}//decimal equivalent of {'\x43', '\x61', '\x66', '\xC3', '\xA9'}
        str := string(byteSlice)
        fmt.Println(str)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    Run in playground

    十进制值也可以工作,上面的程序也将打印Café

    从rune片段创建字符串

    package main
    
    import (  
        "fmt"
    )
    
    func main() {  
        runeSlice := []rune{0x0053, 0x0065, 0x00f1, 0x006f, 0x0072}
        str := string(runeSlice)
        fmt.Println(str)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    Run in playground

    上面的程序中包含了十六进制runeSlice字符串的 Unicode 代码点。程序输出

    Señor  
    
    • 1

    字符串长度

    utf8包RuneCountInString(s string) (n int)的函数可以用来求字符串的长度。该方法接受一个字符串作为参数并返回其中的rune数量。

    正如我们之前讨论的, len(s)用于查找字符串中的字节数,但它不返回字符串长度。

    正如我们已经讨论过的,某些 Unicode 字符的代码点占用的空间超过 1 个字节。使用len找出这些字符串的长度将返回不正确的字符串长度。

    package main
    
    import (  
        "fmt"
        "unicode/utf8"
    )
    
    func main() {  
        word1 := "Señor"
        fmt.Printf("String: %s\n", word1)
        fmt.Printf("Length: %d\n", utf8.RuneCountInString(word1))
        fmt.Printf("Number of bytes: %d\n", len(word1))
    
        fmt.Printf("\n")
        word2 := "Pets"
        fmt.Printf("String: %s\n", word2)
        fmt.Printf("Length: %d\n", utf8.RuneCountInString(word2))
        fmt.Printf("Number of bytes: %d\n", len(word2))
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    Run in playground

    上述程序的输出是

    String: Señor  
    Length: 5  
    Number of bytes: 6
    
    String: Pets  
    Length: 4  
    Number of bytes: 4  
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    上面的输出证实了这一点len(s)RuneCountInString(s)返回不同的值😀。

    字符串比较

    ==运算符用于比较两个字符串是否相等。如果两个字符串相等,则结果为true否则为false

    package main
    
    import (  
        "fmt"
    )
    
    func compareStrings(str1 string, str2 string) {  
        if str1 == str2 {
            fmt.Printf("%s and %s are equal\n", str1, str2)
            return
        }
        fmt.Printf("%s and %s are not equal\n", str1, str2)
    }
    
    func main() {  
        string1 := "Go"
        string2 := "Go"
        compareStrings(string1, string2)
    
        string3 := "hello"
        string4 := "world"
        compareStrings(string3, string4)
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    Run in playground

    compareStrings上面的函数中,使用运算符比较两个字符串str1str2是否相等==。如果它们相等,则打印相应的消息并且函数返回。

    上面的程序打印出,

    Go and Go are equal  
    hello and world are not equal  
    
    • 1
    • 2

    字符串连接

    Go 中有多种执行字符串连接的方法。让我们看一下其中的几个。

    执行字符串连接的最简单方法是使用+运算符。

    package main
    
    import (  
        "fmt"
    )
    
    func main() {  
        string1 := "Go"
        string2 := "is awesome"
        result := string1 + " " + string2
        fmt.Println(result)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    Run in playground

    该程序打印,

    Go is awesome  
    
    • 1

    连接字符串的第二种方法是

    使用fmt 包的Sprintf函数。

    Sprintf函数根据输入格式说明符格式化字符串并返回结果字符串。让我们使用Sprintf函数重写上面的程序。

    package main
    
    import (  
        "fmt"
    )
    
    func main() {  
        string1 := "Go"
        string2 := "is awesome"
        result := fmt.Sprintf("%s %s", string1, string2)
        fmt.Println(result)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    Run in playground

    上面程序中,%s %s是 的格式说明符输入。此格式说明符采用两个字符串作为输入,并且中间有一个空格。这会将两个字符串连接起来,中间有一个空格。结果字符串存储在result. 该程序还打印,

    Go is awesome  
    
    • 1

    字符串是不可变的

    Go 中的字符串是不可变的。一旦创建了字符串,就无法更改它。

    package main
    
    import (  
        "fmt"
    )
    
    func mutate(s string)string {  
        s[0] = 'a'//any valid unicode character within single quote is a rune 
        return s
    }
    func main() {  
        h := "hello"
        fmt.Println(mutate(h))
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    Run in playground

    上面程序的第8行,我们尝试将字符串的第一个字符改为'a'

    单引号内的任何有效 Unicode 字符都是rune。

    我们尝试将a分配到切片的第 0 个位置。这是不允许的,因为字符串是不可变的,因此程序无法编译并出现错误**./prog.go:8:7: 无法分配给 s[0]**

    为了解决这个字符串不可变性,字符串被转换为rune切片。然后,该切片会根据需要进行任何更改,并转换回新字符串。

    package main
    
    import (  
        "fmt"
    )
    
    func mutate(s []rune) string {  
        s[0] = 'a' 
        return string(s)
    }
    func main() {  
        h := "hello"
        fmt.Println(mutate([]rune(h)))
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    Run in playground

    在上述程序中,该mutate函数接受rune切片作为参数。然后它将切片的第一个元素更改为'a',将rune转换回字符串并返回它。

    该程序输出

    aello
    
    • 1
  • 相关阅读:
    冬天这么冷,到底要不要坚持送孩子入托?
    一题多解,ASP.NET Core应用启动初始化的N种方案[上篇]
    Postgresql源码(134)优化器针对volatile函数的排序优化分析
    JVM完整图文学习笔记 (含拓展知识广度学习) 第三章: 类加载与字节码技术
    从函数计算到 Serverless 架构
    C++ Crow web框架使用;升级cmake ;pthread、boost、asio 报错
    修改键盘映射、交换按键
    事务(包含ACID)
    1340. 跳跃游戏 V;2039. 网络空闲的时刻;2767. 将字符串分割为最少的美丽子字符串
    操作系统(一):什么是操作系统
  • 原文地址:https://blog.csdn.net/qq497811258/article/details/133880775