Go 中 len 对字符串和 slice 的返回含义不同
FreeGuideOnline
最新
2026-07-04
go package main
import "fmt"
func main() { s1 := "hello" s2 := "你好" s3 := "👋🌍"
fmt.Println(len(s1)) // 5 (5个英文字母,每个1字节)
fmt.Println(len(s2)) // 6 (2个中文字符,每个3字节)
fmt.Println(len(s3)) // 8 (2个emoji,每个4字节)
}
**核心认知**:一个 Unicode 字符(Go 中称为 `rune`)可能占用 1~4 个字节。`len` 只看字节,不看字符边界。
如果你需要**字符数量**,`len` 会给出错误结果。
## `len` 作用于切片:返回的是**元素个数**
切片是一个动态数组的视图,由指针、长度(length)和容量(capacity)三部分组成。`len(slice)` 返回的是切片当前存储的**元素数量**,这与底层数组的容量无关。
```go
package main
import "fmt"
func main() {
// 创建一个长度为3、容量为5的整数切片
slice := make([]int, 3, 5)
slice[0] = 10
slice[1] = 20
slice[2] = 30
fmt.Println("len:", len(slice)) // 3
fmt.Println("cap:", cap(slice)) // 5
// 追加元素,长度变化,但容量可能不变(如果未超过容量)
slice = append(slice, 40)
fmt.Println("len after append:", len(slice)) // 4
fmt.Println("cap after append:", cap(slice)) // 5(容量足够,未扩容)
}
对切片而言,len 返回的就是你存入的元素个数,与元素类型无关,也不关心每个元素占用多少内存。
对比表格:一图看懂差异
| 类型 | len 的返回值 |
示例 | 输出 | 含义 |
|---|---|---|---|---|
字符串 "Go语言" |
字节数(UTF-8 编码后的总字节数) | len("Go语言") |
8 | G(1) + o(1) + 语(3) + 言(3) |
字符串 "abc" |
字节数(纯ASCII,每字符1字节) | len("abc") |
3 | 恰好等于字符数 |
整数切片 []int{1,2,3,4} |
元素个数 | len([]int{1,2,3,4}) |
4 | 4个 int 元素 |
结构体切片 []struct{A int} |
元素个数 | len([]struct{A int}{{1},{2}}) |
2 | 2个结构体元素 |
如何正确获取字符串的“字符”数量?
你需要的是 Unicode 码点数量,也就是 rune 的个数。常见方法有三种:
方法一:使用 utf8.RuneCountInString (推荐)
标准库 unicode/utf8 提供了专门函数,它遍历字符串并统计码点,无需额外分配内存。
import (
"fmt"
"unicode/utf8"
)
func main() {
s := "你好,世界!"
fmt.Println("字节数:", len(s)) // 18
fmt.Println("字符数:", utf8.RuneCountInString(s)) // 6
}
方法二:转换为 []rune 再取长度
len([]rune(s)) 会先分配一个新的 rune 切片,将字符串解码为一个个 Unicode 码点,再计算切片长度。
s := "Hello, 世界"
runeCount := len([]rune(s))
fmt.Println(runeCount) // 9
这种方法简单但会产生一次内存分配和拷贝,如果只是为了计数,不推荐在性能敏感代码中使用。
方法三:使用 range 遍历计数
for range 循环会按照 rune 遍历字符串,每迭代一次就是一个字符。
s := "🏆Go!"
count := 0
for range s {
count++
}
fmt.Println(count) // 4 ('🏆' + 'G' + 'o' + '!')
一个容易踩坑的实际场景
假设你想截取字符串的前 N 个字符(不是前 N 个字节)。直接用切片表达式 s[:N] 是按字节索引的,可能截断多字节字符,造成乱码。
s := "Go语言"
fmt.Println(s[:4]) // "Go语"?实际输出 "Go��"(截断了“语”的第3个字节)
// 正确做法:先转换为 rune 切片再切片
rs := []rune(s)
fmt.Println(string(rs[:3])) // "Go语"