字节面试:str := “Go 测试“`,如果直接 `str[:3]` 会发生什么

1 阅读2分钟

面试官:str := "Go 测试",如果直接 str[:3] 会发生什么?
候选人:得到 "Go 测"
面试官:❌ 错,是乱码。

这道题考察的是 Go 字符串底层原理UTF-8 编码,属于必知必会的基础坑点。

1️⃣ 错误示范:直接按索引截取

package main

import "fmt"

func main() {
    str := "Go 测试"
    // ❌ 错误:直接按字节截取
    sub := str[:3] 
    fmt.Println(sub) 
}

输出结果:

Go  // 或者乱码,因为"测"字被截断了

2️⃣ 为什么会出现乱码?

🔍 核心原因:Go 字符串是 字节数组

在 Go 中,string 底层是 []byte,索引操作 str[i] 或切片 str[:n] 按字节(byte)计算,而不是按字符(character)计算。

📊 内存布局图解

字符串:"G"  "o"  "测"       "试"
字节数: 1    1    3         3
索引:  [0]  [1]  [2][3][4]  [5][6][7]
                ↑
           str[:3] 截到这里
  • G:ASCII 码,占 1 字节
  • o:ASCII 码,占 1 字节
  • :UTF-8 中文,占 3 字节
  • :UTF-8 中文,占 3 字节

str[:3] 取的是前 3 个字节

  1. G (完整)
  2. o (完整)
  3. 的第 1 个字节 (不完整)

结果:UTF-8 解码器遇到不完整的中文编码,显示为乱码()。

3️⃣ 正确做法:转 []rune 再截取

Rune 是 Go 中的 Unicode 码点类型,一个 rune 代表一个完整的字符。

package main

import "fmt"

func main() {
    str := "Go 测试"
    
    // ✅ 正确:先转 rune 切片,再截取
    runes := []rune(str)
    sub := string(runes[:3]) // 取前 3 个字符
    
    fmt.Println(sub) // 输出:Go 测
}

4️⃣ 面试加分项:性能优化

如果字符串很长,频繁转 []rune 会有内存开销。面试时可以补充:

  1. 短字符串:直接 []rune 转换,代码清晰。
  2. 长字符串/高性能场景:使用 utf8.RuneCountInString 先判断长度,或遍历 range 截取。
// 遍历 range 自动按 rune 解码
for i, r := range str {
    // i 是字节索引,r 是 rune
    if i == 3 { break } // 需配合逻辑控制
}

5️⃣ 一张表总结

操作单位适用场景风险
str[:n]字节 (byte)纯英文/ASCII中文乱码
[]rune(str)[:n]字符 (rune)含中文/多语言内存开销
range str字符 (rune)遍历处理性能较好

💡 记忆口诀

Go 串底层是字节,中文三字节别切。
要想截取不乱码,转成 rune 再操作。


✅ 避坑指南:涉及用户输入、国际化场景,永远不要假设 1 字符 = 1 字节