Go语言字符串处理

字符串是编程中最常用的数据类型之一。Go语言中的字符串是只读的字节切片,支持UTF-8编码。标准库中的strings、strconv、unicode、regexp等包提供了强大的字符串处理能力。本章将系统介绍字符串的常用操作,以及高效处理字符串的最佳实践。

核心知识点:Go字符串不可变、支持索引但返回字节;rune代表Unicode码点;使用strings.Builder高效拼接字符串。

1. 字符串基础回顾

字符串本质上是只读的字节序列。使用len()获取字节长度,而非字符数量。如需遍历字符,应使用for range。

字符串的字节与字符
package main

import "fmt"

func main() {
    s := "Go语言"
    fmt.Println("字节长度:", len(s))               // 8 (G o 语 言 每个中文字符占3字节)
    fmt.Println("字符数:", len([]rune(s)))       // 4
    for i, ch := range s {                     // range 按 rune 遍历
        fmt.Printf("%d: %c\n", i, ch)
    }
}

2. strings 包常用函数

2.1 查找与存在判断

import "strings"

s := "hello world"
fmt.Println(strings.Contains(s, "world"))      // true
fmt.Println(strings.ContainsAny(s, "abc"))     // false
fmt.Println(strings.HasPrefix(s, "he"))        // true
fmt.Println(strings.HasSuffix(s, "ld"))        // true
fmt.Println(strings.Index(s, "o"))             // 4 (第一次出现)
fmt.Println(strings.LastIndex(s, "o"))         // 7
fmt.Println(strings.Count(s, "l"))             // 3

2.2 大小写转换

upper := strings.ToUpper("hello")      // HELLO
lower := strings.ToLower("WORLD")      // world
title := strings.Title("hello world")  // Hello World (注意:仅单词首字母大写)

2.3 去除空白(Trim)

s := "  hello  "
fmt.Println(strings.TrimSpace(s))          // "hello"
fmt.Println(strings.Trim(s, " "))           // 同上
fmt.Println(strings.TrimLeft(s, " "))       // "hello  "
fmt.Println(strings.TrimRight(s, " "))      // "  hello"
fmt.Println(strings.TrimPrefix("file.txt", "file."))  // "txt"

2.4 分割与合并

// 分割
arr := strings.Split("a,b,c", ",")         // []string{"a","b","c"}
arr2 := strings.SplitN("a,b,c", ",", 2)    // []string{"a","b,c"}
arr3 := strings.Fields("hello   world")    // 按空白分割,自动忽略多余空格: ["hello","world"]

// 合并
joined := strings.Join(arr, "-")           // "a-b-c"

2.5 替换与重复

// 替换 (n为-1表示全部替换)
newStr := strings.Replace("go go go", "go", "Go", 2)   // "Go Go go"
newStr2 := strings.ReplaceAll("go go", "go", "Go")     // "Go Go"
repeated := strings.Repeat("na", 3)                    // "nanana"

3. 字符串与数值转换 (strconv)

转换示例
import "strconv"

// 字符串转整数
i, err := strconv.Atoi("123")               // 123, nil
i2, err := strconv.ParseInt("ff", 16, 64)   // 255, nil
u, err := strconv.ParseUint("100", 10, 64)  // 100, nil

// 整数转字符串
s := strconv.Itoa(456)                      // "456"
s2 := strconv.FormatInt(255, 16)            // "ff"

// 浮点数转换
f, err := strconv.ParseFloat("3.1415", 64)  // 3.1415, nil
s3 := strconv.FormatFloat(3.1415, 'f', -1, 64) // "3.1415"

// 布尔转换
b, err := strconv.ParseBool("true")         // true, nil
s4 := strconv.FormatBool(true)              // "true"

4. 高效字符串拼接

使用+拼接大量字符串效率低下。推荐使用strings.Builder(Go 1.10+)。

Builder示例
var builder strings.Builder
builder.WriteString("Hello")
builder.WriteString(", ")
builder.WriteString("World")
builder.WriteByte('!')
result := builder.String()   // "Hello, World!"
fmt.Println(result)

// 预分配容量可提高效率
builder.Grow(100) // 预分配100字节容量
对于少量拼接(<5次),直接使用+或fmt.Sprintf也可;大量拼接或循环中务必使用strings.Builder。

5. 正则表达式

regexp包提供了强大的模式匹配功能。

正则示例
import "regexp"

// 匹配测试
matched, _ := regexp.MatchString(`\d+`, "abc123def") // true

// 编译正则表达式
re := regexp.MustCompile(`\d+`)
// 查找首个匹配
first := re.FindString("abc123def456")      // "123"
// 查找所有匹配
all := re.FindAllString("a1b2c3", -1)       // ["1","2","3"]
// 替换
replaced := re.ReplaceAllString("a1b2c3", "X") // "aXbXcX"
// 分组提取
reEmail := regexp.MustCompile(`(\w+)@(\w+)\.com`)
parts := reEmail.FindStringSubmatch("user@example.com")
fmt.Println(parts[1]) // "user"

6. 字节切片处理

对于字节数据,使用bytes包,其API与strings包类似,但操作[]byte。

import "bytes"

b := []byte("hello world")
contains := bytes.Contains(b, []byte("world"))
split := bytes.Split(b, []byte(" "))

7. Unicode 处理

unicode/utf8和unicode包提供UTF-8编解码及字符类别判断。

import "unicode/utf8"

s := "Go语言"
fmt.Println(utf8.RuneCountInString(s))      // 4
fmt.Println(utf8.ValidString(s))            // true

// 判断字符类别
import "unicode"
for _, ch := range s {
    if unicode.IsLetter(ch) {
        fmt.Printf("%c 是字母\n", ch)
    }
}

综合示例:简单的日志分析器

综合运用字符串处理
package main

import (
    "fmt"
    "strings"
)

func main() {
    logLines := []string{
        "[INFO] 2025-01-15 服务启动",
        "[ERROR] 2025-01-15 数据库连接失败",
        "[INFO] 2025-01-15 重试连接",
        "[ERROR] 2025-01-15 超时",
        "[WARN] 2025-01-15 配置过期",
    }

    var infoCount, errorCount int
    for _, line := range logLines {
        switch {
        case strings.Contains(line, "[INFO]"):
            infoCount++
        case strings.Contains(line, "[ERROR]"):
            errorCount++
        }
        // 提取日期(假设格式固定)
        if idx := strings.Index(line, "2025"); idx != -1 {
            date := strings.Fields(line)[1] // 第二个字段为日期
            fmt.Println("日志日期:", date)
        }
    }
    fmt.Printf("统计: INFO=%d, ERROR=%d\n", infoCount, errorCount)

    // 高效拼接统计结果
    var builder strings.Builder
    builder.WriteString("最终报告: ")
    builder.WriteString(fmt.Sprintf("INFO:%d, ERROR:%d", infoCount, errorCount))
    fmt.Println(builder.String())
}
日志日期: 2025-01-15
日志日期: 2025-01-15
日志日期: 2025-01-15
日志日期: 2025-01-15
日志日期: 2025-01-15
统计: INFO=2, ERROR=2
最终报告: INFO:2, ERROR:2
小结:Go语言的标准库为字符串处理提供了丰富且高效的函数。熟练掌握strings、strconv、regexp等包,可以轻松应对文本处理、日志解析、数据校验等需求。下一章我们将学习Go语言中的JSON处理。