Go 语言中字符串基本操作

这篇文章已经放到腾讯智能工作台的知识库啦,链接在这里:[ima.copilot-Go 入门到入土](https://ima.qq.com/wiki/?shareId=6467e4feb4e225ab1acf92d9134ba4baeaf578ed327f9ba285da311f9ea4742a)。要是你有啥不懂的地方,就去知识库找 AI 聊一聊吧。 本篇将详细讲解 Go 语言中与字符串相关的操作。 # 1、rune 和 字符串长度 ## 1、Go 函数语法约定 在开始之前,需要注意 Go 语言的一个语法规范。函数或流程控制语句的左花括号 `{` **必须**与函数名或关键字在同一行,不能换行书写。 ![image.png](https://pic.code-nav.cn/post_picture/1620630456775032833/XfZTqLUQaiEoCDIp.webp) 这种强制性规定统一了代码风格,避免了不同开发者因个人习惯造成代码格式的混乱。 ## 2、`len()` 计算字符串长度 Go 语言提供了一个内置函数 `len()`,可以方便地计算出字符串的长度。 ![image.png](https://pic.code-nav.cn/post_picture/1620630456775032833/7lu7bQc5ELd6OXi5.webp) 在上述代码中,`len()` 函数直接返回了字符串 `name` 的长度。这是一个非常便捷和常见的操作。 ## 3、`len()` 的本质:字节数 `len()` 函数虽然通用,但它返回的是字符串所占的**字节数(the number of bytes)**,而不是字符数。 当字符串只包含英文字母、数字或标准 ASCII 符号时,一个字符恰好占用一个字节,此时字节数等于字符数。但当字符串中包含非 ASCII 字符(如中文、日文等)时,情况就有所不同。 在 **UTF-8** 编码(Go 语言默认的字符串编码)中: - 一个英文字符占用 **1** 个字节。 - 一个中文字符通常占用 **3** 个字节。 ![image.png](https://pic.code-nav.cn/post_picture/1620630456775032833/lfi6QgMoibZHZuMb.webp) 直观上看,字符串 "叫我阿杰好l" 包含 6 个字符。但 `len()` 的计算结果为什么是 16 呢? 因为计算的是字节数:`1 (l) + 5*3 (叫我阿杰好) = 16` 个字节。 ## 4、如何获取真实的字符数? 在很多业务场景中,我们关心的是字符串实际包含多少个字符,而不是它占用了多少字节。为了解决这个问题,我们需要将字符串转换为 `rune` 切片。 `rune` 类型是 Go 语言中用于表示单个 Unicode 字符的特殊类型。将字符串转换为 `[]rune` 后,每个中文字符和英文字符都会被当作一个独立的元素。此时再使用 `len()` 函数,就能得到准确的字符数量。 ![image.png](https://pic.code-nav.cn/post_picture/1620630456775032833/8ffs04bSQ0FWq5zT.webp) ## 5、总结 在 Go 语言中计算字符串长度时,必须注意以下关键点: 1. 如果你的字符串**只包含英文或 ASCII 字符**,或者你关心的就是**字节长度**,可以直接使用 `len(str)`。 2. 如果你需要知道字符串中实际的**字符数量**(特别是处理包含中文等多字节字符的场景),必须先将字符串转换为 `rune` 切片 `[]rune(str)`,然后再对其使用 `len()` 函数。 掌握这个细节对于正确处理多语言文本至关重要。 # 2、转义符详解 ## 1、 为什么需要转义符? 假设我们需要定义一个字符串,其内容本身就包含特殊字符,例如双引号。 如果我们直接这样编写代码: ![image.png](https://pic.code-nav.cn/post_picture/1620630456775032833/KOGZvUwzMlizcCTC.webp) 编译器会报错。因为在 Go 语言中,双引号用于界定字符串的开始和结束。上述代码会被编译器误解为:第一个字符串是 `"Go "`,第二个字符串是 `""`,而中间的 `体系课` 则是不合法的语法。 为了在字符串内部正确地包含双引号等特殊字符,我们就需要使用转义符。 ## 2、使用反斜杠 `\` 进行转义 在 Go 中,反斜杠 `\` 是核心的转义符。当它出现在一个特殊字符前时,它会“转义”该字符,使其失去原有的特殊含义,而被当作一个普通的字符来处理。 **示例:** 要在一个由双引号定义的字符串中包含双引号,我们可以在内部的双引号前加上 `\`: ![image.png](https://pic.code-nav.cn/post_picture/1620630456775032833/zMkqb0j5KdG5LDjs.webp) 在这里,`\"` 被编译器视为一个整体,并被解释为单个双引号字符。 ## 2、使用反引号 `` ` `` 创建原生字符串 除了使用转义符,Go 语言还提供了一种更简洁的方式来处理包含大量特殊字符的字符串:**原生字符串字面量 (Raw String Literal)**。这种字符串使用反引号 `` ` `` (通常位于键盘 Tab 键的上方) 来定义。 在反引号包裹的字符串中,所有的字符都按其字面意义进行解释,**无需任何转义**。 ![image.png](https://pic.code-nav.cn/post_picture/1620630456775032833/1Jh5cTjnEdmtUKlF.webp) 这种写法的输出结果与使用转义符完全相同。反引号内的内容可以随意编写,包括换行符和双引号,这与 Python 中的三引号字符串有些类似,非常适合定义多行文本或包含复杂特殊字符的文本。 ## 3、常见的转义序列 无论是否使用原生字符串,理解并掌握常见的转义序列都至关重要。转义符 `\` 可以与多个字符组合,形成具有特殊含义的序列。这些序列通常与 ASCII 控制码相对应。 以下是一些最常用的转义序列: |序列|含义|描述| |---|---|---| |`\n`|换行符|将光标移动到下一行的开头 (Line Feed)| |`\r`|回车符|将光标移动到当前行的开头 (Carriage Return)| |`\t`|水平制表符|相当于按下 Tab 键,用于对齐文本| |`\\`|反斜杠|在字符串中插入一个反斜杠字符 `\`| |`\"`|双引号|在字符串中插入一个双引号字符 `"`| |`\'`|单引号|在字符串中插入一个单引号字符 `'`| |`\?`|问号|在字符串中插入一个问号字符 `?`| 其中,`\r\n` 组合(回车并换行)在 Windows 系统中常被用作标准的换行符,而 `\n` 在 Unix/Linux 和 macOS 中更为常见。 ![image.png](https://pic.code-nav.cn/post_picture/1620630456775032833/tqq8VzpfpoKG8XOi.webp) ## 4、`fmt.Print` 与 `fmt.Println` 的区别 在 Go 中,标准库 `fmt` 提供了多种打印函数,其中 `Print` 和 `Println` 的一个关键区别在于是否自动换行。 - `fmt.Println`:在打印完所有参数后,**会自动在末尾添加一个换行符**。 - `fmt.Print`:仅打印传入的参数,**不会在末尾添加任何内容**。 ![image.png](https://pic.code-nav.cn/post_picture/1620630456775032833/q1lDM6v78Z1Exbfm.webp) 如果我们想用 `Print` 实现与 `Println` 相同的换行效果,就需要手动添加转义符 `\n` 或 `\r\n`。 ![image.png](https://pic.code-nav.cn/post_picture/1620630456775032833/MsRT5bKf7tNcW7xa.webp) 掌握转义符是 Go 语言编程的基础。无论是为了在字符串中嵌入特殊字符,还是为了控制输出格式,理解 `\` 和 `` ` `` 的用法都至关重要。在后续的开发中,我们会频繁地遇到这些概念,熟练运用它们将极大提升编码效率和代码的可读性。 # 3、格式化输出 在实际开发中,我们常常需要将不同类型的变量(如字符串、整数、浮点数等)组合成一个完整的字符串进行输出。 ## 1、字符串拼接的挑战 使用简单的 `+` 号拼接字符串,在处理复杂或多类型数据时会变得非常繁琐且难以维护。 ![image.png](https://pic.code-nav.cn/post_picture/1620630456775032833/TiCLCLMsjZjPGIZC.webp) 可以看到,这种方式不仅代码可读性差,还需要手动进行类型转换(如 `strconv.Itoa`),非常不便。 ## 2、使用 `fmt.Printf` 进行格式化输出 为了解决上述问题,Go 语言提供了 `fmt.Printf` 函数。它允许我们使用一个**模板字符串**和一系列**占位符**(也称格式化动词),将变量优雅地嵌入到字符串中。 **`Printf` 的工作方式:** 1. 定义一个包含占位符的模板字符串。 2. 在字符串后面,按顺序提供与占位符对应的变量。 3. 函数会自动将变量替换到相应的位置,并输出到控制台。 **示例:** ![image.png](https://pic.code-nav.cn/post_picture/1620630456775032833/5Hykj8e1uSm2Ra84.webp) 这段代码不仅更易读、更易维护,而且 Go 会自动处理类型,我们无需再关心 `int` 到 `string` 的转换。注意 `Printf` **不会自动换行**,因此我们在模板末尾手动添加了 `\n`。 ## 3、生成格式化字符串:`fmt.Sprintf` 有时我们需要的不是直接打印到控制台,而是将格式化后的结果保存为一个字符串变量。这时可以使用 `fmt.Sprintf`。 它的用法与 `Printf` 完全相同,唯一的区别是它会**返回一个格式化好的字符串**,而不是将其打印出来。 ![image.png](https://pic.code-nav.cn/post_picture/1620630456775032833/DLVXY2lYm5O6LCk5.webp) 这个函数在构建日志信息、生成 API 响应或任何需要先处理再输出的场景中非常有用。 ## 4、性能说明 通常情况下,`fmt.Printf` 和 `fmt.Sprintf` 是构建字符串的首选方法,因为它们极大地提升了代码的可读性和可维护性。但在对性能有极端要求的场景中,手动的字符串拼接(如使用 `strings.Builder`)可能会有更好的性能表现。对于绝大多数应用,可读性带来的好处远超微小的性能差异。 ## 5、常用格式化占位符 Go 提供了丰富的占位符来控制输出格式。以下是一些最常用的占位符: | 占位符 | 描述 | 常用类型 | | ---------- | -------------------------------- | --------------- | | **通用** | | | | `%v` | **默认格式的值**。对于不同类型,会以最合适的方式展示。 | 任何类型 | | `%+v` | 在 `%v` 的基础上,打印结构体时会额外输出字段名。 | `struct` | | `%#v` | **Go 语法表示的值**。输出结果是符合 Go 语法的字面量。 | 任何类型 | | `%T` | **值的类型**。输出变量的类型信息。 | 任何类型 | | **字符串** | | `string` | | `%s` | 普通的字符串或 `[]byte` | `string` | | `%q` | 为字符串**加上双引号**,并对特殊字符进行安全转义。 | `string` | | **整数** | | `int`, `uint` 等 | | `%d` | 十进制表示 | `int` | | `%b` | 二进制表示 | `int` | | `%o` | 八进制表示 | `int` | | `%x`, `%X` | 十六进制表示 (分别为小写 `a-f` 和大写 `A-F`) | `int` | | **浮点数** | | `float` | | `%f` | 标准小数表示 (例如 `123.456`) | `float` | | `%e`, `%E` | 科学计数法表示 (例如 `1.23456e+02`) | `float` | | **布尔值** | | `bool` | | `%t` | `true` 或 `false` | `bool` | | **指针** | | 指针类型 | | `%p` | 指针的十六进制表示(以 `0x` 开头) | 指针 | **宽度与对齐示例:** 你还可以控制输出的宽度和对齐方式。 ![image.png](https://pic.code-nav.cn/post_picture/1620630456775032833/8h5wwie0zuo35cYS.webp) 掌握格式化输出是 Go 语言开发者的必备技能。`fmt.Printf` 和 `fmt.Sprintf` 提供了一种强大而灵活的方式来构建和展示字符串,使得代码更加简洁和专业。在日常开发中,应优先选择这些函数来处理字符串格式化任务。 # 4、高性能字符串拼接 当需要拼接大量字符串,尤其是在循环中,性能就成为一个重要的考量因素。此时,`strings.Builder` 是最佳选择。它的性能远高于 `+` 拼接和 `fmt.Sprintf`。 `strings.Builder` 通过维护一个内部的字节缓冲区(buffer)来避免每次拼接都重新分配内存,从而实现高效的字符串构建。 **使用步骤:** 1. 创建一个 `strings.Builder` 实例。 2. 使用 `WriteString()` 或 `Write()` 等方法向其内部缓冲区追加内容。 3. 最后调用 `String()` 方法获取最终拼接完成的字符串。 ![image.png](https://pic.code-nav.cn/post_picture/1620630456775032833/GblycPT900HFGSRH.webp) 虽然 `strings.Builder` 的代码看起来比 `Sprintf` 繁琐一些,但在性能敏感的场景下,它是构建字符串的不二之选。 ## 如何选择拼接方式 掌握不同场景下最合适的字符串拼接方法,是编写高效、可维护 Go 代码的关键。 - **`+` 拼接**:仅适用于少量、简单的字符串连接。 - **`fmt.Sprintf`**:**日常开发首选**。代码可读性最高,使用最方便,足以应对绝大多数场景。 - **`strings.Builder`**:**性能要求高的场景首选**。当程序中有大量或频繁的字符串拼接操作(例如在循环内部)时,应优先使用 `Builder` 以获得最佳性能。 # 5、字符串的比较 在 Go 中,比较字符串是一项直接且常用的操作。 ## 1、等于 (`==`) 和不等于 (`!=`) 你可以使用标准的比较运算符 `==` 和 `!=` 来判断两个字符串是否完全相同。 ![image.png](https://pic.code-nav.cn/post_picture/1620630456775032833/nwuWB3kVnH8yCCYI.webp) ## 2、大于 (`>`) 和小于 (`<`) Go 语言同样支持使用 `>`、`<`、`>=` 和 `<=` 来比较字符串的大小。 **比较规则:** 字符串的比较是**按字典顺序**进行的。它会从左到右逐个比较两个字符串中对应位置字符的 **ASCII 码值(或更准确地说是 Unicode 码点)**。一旦发现差异,比较立即结束并返回结果。 ![image.png](https://pic.code-nav.cn/post_picture/1620630456775032833/K2NfkNmqB6RcN5vw.webp) # 6、字符串操作常用方法 Go 的标准库 `strings` 提供了大量实用、高效的字符串处理函数。要使用它们,首先需要导入该包: ```go import ( "strings" ) ``` 下面我们介绍一些最常用的函数。 ## 1、包含与计数 - **`strings.Contains(s, substr)`**: 判断字符串 `s` 中是否包含子串 `substr`。 - **`strings.Count(s, substr)`**: 计算子串 `substr` 在字符串 `s` 中出现的次数。 ![image.png](https://pic.code-nav.cn/post_picture/1620630456775032833/nvpXBLN34dPOU9Dv.webp) ## 2、分割与连接 - **`strings.Split(s, sep)`**: 使用分隔符 `sep` 将字符串 `s` 分割成一个字符串切片。 ![image.png](https://pic.code-nav.cn/post_picture/1620630456775032833/2ZiFb9pC62Ygh5W3.webp) 如果分隔符不存在,`Split` 会返回一个只包含原字符串的切片。 ## 3、前缀与后缀 - **`strings.HasPrefix(s, prefix)`**: 判断字符串 `s` 是否以 `prefix` 开头。 - **`strings.HasSuffix(s, suffix)`**: 判断字符串 `s` 是否以 `suffix` 结尾。 ![image.png](https://pic.code-nav.cn/post_picture/1620630456775032833/PbfRCp2vKjSGNSTd.webp) ## 4、查找位置 - **`strings.Index(s, substr)`**: 查找子串 `substr` 在字符串 `s` 中首次出现的位置(字节索引)。如果不存在,则返回 -1。 ![image.png](https://pic.code-nav.cn/post_picture/1620630456775032833/lqk8vDAA4r9yvmNa.webp) **注意**:`Index` 返回的是字节位置。对于包含多字节字符(如中文)的字符串,这个位置可能不等于字符的个数。 ## 5、替换 - **`strings.Replace(s, old, new, n)`**: 将字符串 `s` 中的 `old` 子串替换为 `new`。参数 `n` 控制替换次数: - `n < 0` (通常用 `-1`): 全部替换。 - `n > 0`: 最多替换 `n` 次。 ![image.png](https://pic.code-nav.cn/post_picture/1620630456775032833/nfKTpbXqLN5MKCFy.webp) ## 6、大小写转换 - **`strings.ToLower(s)`**: 将字符串转换为全小写。 - **`strings.ToUpper(s)`**: 将字符串转换为全大写。 ![image.png](https://pic.code-nav.cn/post_picture/1620630456775032833/H8BdEwtvAR2YZpyP.webp) ![image.png](https://pic.code-nav.cn/post_picture/1620630456775032833/KUHgBRjiPSlwQ77a.webp) ## 7、修剪字符 - **`strings.Trim(s, cutset)`**: 从字符串 `s` 的两端移除 `cutset` 中包含的任意字符。 - **`strings.TrimSpace(s)`**: 移除字符串两端的空白字符(空格、制表符、换行符等),这是最常用的修剪函数。 - **`strings.TrimLeft(s, cutset)`** 和 **`strings.TrimRight(s, cutset)`**: 分别只从左边或右边修剪。 ![image.png](https://pic.code-nav.cn/post_picture/1620630456775032833/7XdJLoBizfpipenW.webp)

下载 APP