Go 字符串基础

GolangGolangBeginner
立即练习

💡 本教程由 AI 辅助翻译自英文原版。如需查看原文,您可以 切换至英文原版

介绍

在上一课中,我们学习了 Go 语言中的字符使用 UTF-8 编码,并以 byterune 类型存储。现在,让我们讨论字符串,它是字符的集合。让我们一起探讨这个话题。

知识点:

  • 什么是字符串
  • 创建字符串
  • 声明字符串
  • 常见的字符串函数
  • 访问字符串元素

Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL go(("`Golang`")) -.-> go/BasicsGroup(["`Basics`"]) go(("`Golang`")) -.-> go/DataTypesandStructuresGroup(["`Data Types and Structures`"]) go(("`Golang`")) -.-> go/FunctionsandControlFlowGroup(["`Functions and Control Flow`"]) go(("`Golang`")) -.-> go/ObjectOrientedProgrammingGroup(["`Object-Oriented Programming`"]) go(("`Golang`")) -.-> go/AdvancedTopicsGroup(["`Advanced Topics`"]) go/BasicsGroup -.-> go/values("`Values`") go/BasicsGroup -.-> go/variables("`Variables`") go/DataTypesandStructuresGroup -.-> go/strings("`Strings`") go/FunctionsandControlFlowGroup -.-> go/for("`For`") go/FunctionsandControlFlowGroup -.-> go/functions("`Functions`") go/ObjectOrientedProgrammingGroup -.-> go/struct_embedding("`Struct Embedding`") go/AdvancedTopicsGroup -.-> go/text_templates("`Text Templates`") go/AdvancedTopicsGroup -.-> go/number_parsing("`Number Parsing`") subgraph Lab Skills go/values -.-> lab-149069{{"`Go 字符串基础`"}} go/variables -.-> lab-149069{{"`Go 字符串基础`"}} go/strings -.-> lab-149069{{"`Go 字符串基础`"}} go/for -.-> lab-149069{{"`Go 字符串基础`"}} go/functions -.-> lab-149069{{"`Go 字符串基础`"}} go/struct_embedding -.-> lab-149069{{"`Go 字符串基础`"}} go/text_templates -.-> lab-149069{{"`Go 字符串基础`"}} go/number_parsing -.-> lab-149069{{"`Go 字符串基础`"}} end

什么是字符串

在我们学习的第一个 Go 程序中,我们打印了字符串 "hello, world"

字符串是 Go 语言中的一种基本数据类型,也称为字符串字面量。它可以理解为一个字符的集合,并占据一块连续的内存空间。这块内存可以存储任何类型的数据,例如字母、文本、表情符号等。

然而,与其他语言不同,Go 中的字符串是不可变的,无法被修改。这意味着一旦字符串被创建,你就无法更改其中的单个字符。如果你需要一个修改后的字符串版本,必须创建一个新的字符串。

创建字符串

字符串可以通过多种方式声明。让我们来看第一种方法。创建一个名为 string.go 的新文件:

touch ~/project/string.go

编写以下代码:

package main

import "fmt"

func main() {
    // 使用 var 关键字创建字符串变量 a
    var a string = "labex"
    a = "labex" // 将 "labex" 赋值给变量 a

    // 声明变量 b 并赋值
    var b string = "labs"

    // 可以省略类型声明
    var c = "Monday"

    // 使用 := 快速声明并赋值
    d := "Sunday"
    fmt.Println(a, b, c, d)
}

以上代码展示了如何使用 var 关键字和 := 操作符创建字符串。如果你在使用 var 创建变量时赋值,可以省略类型声明,如变量 c 的创建所示。:= 操作符是 Go 中声明并初始化变量的简写形式。它会根据赋值的值自动推断变量的类型。使用它可以让你的代码更加简洁。

go run string.go

预期输出如下:

labex labs Monday Sunday

声明字符串

在大多数情况下,我们使用双引号 "" 来声明字符串。双引号的优点是可以用作转义序列。例如,在下面的程序中,我们使用 \n 转义序列来创建新行:

package main

import "fmt"

func main() {
    x := "linux\nlabex"
    fmt.Println(x)
}
go run string.go

预期输出如下:

linux
labex

以下是一些常见的转义序列:

符号 描述
\n 换行
\r 回车
\t 制表符
\b 退格
\\ 反斜杠
\' 单引号
\" 双引号

如果你想保留文本的原始格式或需要使用多行文本,可以使用反引号来表示:

package main

import "fmt"

func main() {
    // 输出 "labex" 的 ASCII 艺术
    ascii := `
        ##        #####   #########  ########## ##    ##         ##       ##  ##  ##    ## ##       ##  ##         ##      ##    ## #########  #########    ###         ##      ########## ##    ## ##        ###         ##      ##    ## ##    ## ##       ##  ##         ########## ##    ## #########  ########## ##    #`
    fmt.Println(ascii)
}

反引号通常用于提示信息、HTML 模板以及其他需要保留输出原始格式的场景。反引号内的文本被视为原始字符串字面量,这意味着转义序列不会被解释。这使得包含多行文本和特殊字符变得非常方便,而无需对它们进行转义。

获取字符串的长度

在上一课中,我们学习了英文字符和一般标点符号占用一个字节。

因此,在 Go 中,我们可以使用 len() 函数来获取字符串的字节长度。如果字符串中没有占用多个字节的字符,len() 函数可以准确地测量字符串的长度。

如果字符串中包含占用多个字节的字符,你可以使用 utf8.RuneCountInString 函数来获取字符串的实际字符数。

让我们看一个例子。将以下代码写入 string.go 文件:

package main

import (
    "fmt"
    "unicode/utf8"
)

func main() {
    // 使用 var 和 := 声明两个空字符串
    var a string
    b := ""

    c := "labex"
    d := "abc"


    // 输出字节长度
    fmt.Printf("The value of a is %s, the byte length of a is: %d\n", a, len(a))
    fmt.Printf("The value of b is %s, the byte length of b is: %d\n", b, len(b))
    fmt.Printf("The value of c is %s, the byte length of c is: %d\n", c, len(c))
	fmt.Printf("The value of d is %s, the byte length of d is: %d\n", d, len(d))


    // 输出字符串长度
    fmt.Printf("The length of c is: %d\n", utf8.RuneCountInString(c))
	fmt.Printf("The length of d is: %d\n", utf8.RuneCountInString(d))
}
go run string.go

预期输出如下:

The value of a is , the byte length of a is: 0
The value of b is , the byte length of b is: 0
The value of c is labex, the byte length of c is: 5
The value of d is abc, the byte length of d is: 3
The length of c is: 5
The length of d is: 3

在程序中,我们首先声明了两个空字符串以及字符串 labexabc。你可以看到它们的字节长度和实际长度是相同的,因为它们只包含单字节字符。

访问字符串元素

由于字符串本质上是字节序列,我们可以使用索引访问字符串中的单个字节或字符。在 Go 中,字符串的索引从 0 开始,类似于数组。

然而,需要注意的是,使用索引访问字符串元素返回的是 byte,而不是字符(rune)。如果字符串包含多字节字符,使用索引时需要小心,因为你可能无法获取完整的字符。为了安全地获取单个字符(rune),你可以使用 for...range 循环遍历字符串,它会正确处理 UTF-8 编码。

让我们将以下代码添加到 string.go 文件中:

package main

import (
	"fmt"
	"unicode/utf8"
)

func main() {
	str := "Hello, world!"

	// 通过索引访问字节
	fmt.Printf("Index 0 处的字节: %c\n", str[0]) // 输出: H
	fmt.Printf("Index 7 处的字节: %c\n", str[7]) // 输出: w

    // 使用 for...range 遍历以安全获取 rune
    fmt.Println("遍历 rune:")
    for index, char := range str {
        fmt.Printf("Index: %d, Char: %c\n", index, char)
    }

	// 获取 rune 的数量
	fmt.Printf("字符串中的 rune(字符)数量: %d\n", utf8.RuneCountInString(str))

}

现在运行程序:

go run string.go

输出如下:

Index 0 处的字节: H
Index 7 处的字节: w
遍历 rune:
Index: 0, Char: H
Index: 1, Char: e
Index: 2, Char: l
Index: 3, Char: l
Index: 4, Char: o
Index: 5, Char: ,
Index: 6, Char:
Index: 7, Char: w
Index: 8, Char: o
Index: 9, Char: r
Index: 10, Char: l
Index: 11, Char: d
Index: 12, Char: !
字符串中的 rune(字符)数量: 13

在这个输出中,你可以看到:

  1. 通过特定索引访问字符串返回的是字节。
  2. 使用 for...range 循环可以正确地遍历 rune。

这个示例突出了字节和 rune 之间的关键区别,以及在处理 Go 字符串中的字符时使用适当方法的重要性。

字符串与整数的转换

我们可以使用 strconv 包中的函数来实现字符串和整数之间的转换:

package main

import (
    "fmt"
    "strconv"
)

func main() {
    // 声明一个字符串 a 和一个整数 b
    a, b := "233", 223

    // 使用 Atoi 将字符串转换为整数
    c, _ := strconv.Atoi(a)

    // 分别使用 Sprintf 和 Itoa 函数
    // 将整数转换为字符串
    d1 := fmt.Sprintf("%d", b)
    d2 := strconv.Itoa(b)

    fmt.Printf("a 的类型: %T\n", a)   // string
    fmt.Printf("b 的类型: %T\n", b)   // int
    fmt.Printf("c 的类型: %T\n", c)   // int
    fmt.Printf("d1 的类型: %T\n", d1) // string
    fmt.Printf("d2 的类型: %T\n", d2) // string
}
go run string.go

预期输出如下:

a 的类型: string
b 的类型: int
c 的类型: int
d1 的类型: string
d2 的类型: string

在程序中,我们使用了 fmt 包中的 Sprintf() 函数,其格式如下:

func Sprintf(format string, a ...interface{}) string

format 是一个包含转义序列的字符串,a 是为转义序列提供值的常量或变量,... 表示可以有多个与 a 类型相同的变量。函数后的字符串表示 Sprintf 返回一个字符串。以下是使用该函数的示例:

a = fmt.Sprintf("%d+%d=%d", 1, 2, 3)
fmt.Println(a) // 1+2=3

在这段代码中,format 传递了三个整数变量 1、2 和 3。format 中的 %d 整数转义字符被整数值替换,Sprintf 函数返回替换后的结果 1+2=3

此外,需要注意的是,当使用 strconv.Atoi() 将字符串转换为整数时,该函数会返回两个值:转换后的整数 val 和错误代码 err。因为在 Go 中,如果你声明了一个变量,就必须使用它,所以我们可以使用下划线 _ 来忽略 err 变量。

strconv.Atoi() 正确转换时,err 返回 nil。当转换过程中发生错误时,err 返回错误信息,而 val 的值将为 0。你可以更改字符串 a 的值,并将下划线替换为普通变量来亲自尝试。这是错误处理的一个良好实践,而错误处理是 Go 编程的关键部分。

字符串拼接

拼接两个或多个字符串的最简单方法是使用 + 操作符。我们也可以使用 fmt.Sprintf() 函数来拼接字符串。让我们看一个例子:

package main

import (
    "fmt"
)

func main() {
    a, b := "lab", "ex"
    // 使用最简单的方法 + 进行拼接
    c1 := a + b
    // 使用 Sprintf 函数进行拼接
    c2 := fmt.Sprintf("%s%s", a, b)
    fmt.Println(a, b, c1, c2) // lab ex labex labex
}
go run string.go

预期输出如下:

lab ex labex labex

在程序中,我们还使用了 fmt 包中的 Sprintf() 函数来拼接字符串并打印结果。这两种方法都是常见的字符串拼接方式,选择哪种方法通常取决于可读性和个人偏好。

去除字符串的首尾空格

我们可以使用 strings.TrimSpace 函数来去除字符串的首尾空格。该函数接受一个字符串作为输入,并返回去除首尾空格后的字符串。其格式如下:

func TrimSpace(s string) string

以下是一个示例:

package main

import (
    "fmt"
    "strings"
)

func main() {
    a := " \t \n  labex \n \t labs"
    fmt.Println(strings.TrimSpace(a))
}
go run string.go

预期输出如下:

labex
         labs

注意,strings.TrimSpace() 只会去除字符串开头和结尾的空格,字符串内部的空格保持不变。

总结

总结一下我们在这节课中学到的内容:

  • 字符串与字符之间的关系
  • 声明字符串的两种方式:使用双引号和反引号。
  • 使用索引(字节访问)和 for...range(rune 访问)访问字符串元素。
  • 使用 len()(字节长度)和 utf8.RuneCountInString(字符/rune 长度)获取字符串的长度。
  • 使用 strconv.Atoi()strconv.Itoa() 进行字符串与整数的转换。
  • 使用 + 操作符和 fmt.Sprintf() 拼接字符串。
  • 使用 strings.TrimSpace() 去除字符串的首尾空格。

在这节课中,我们解释了日常生活中使用的字符串。我们学习了字符串与字符之间的关系,掌握了字符串的创建和声明,并了解了一些常见的字符串函数。我们还学习了如何安全地访问字符串中的单个字符,尤其是在处理多字节字符时,并掌握了一些关键的字符串操作方法。这为你处理 Go 中的字符串数据打下了坚实的基础。

您可能感兴趣的其他 Golang 教程