rune代表Unicode码点(逻辑字符),byte代表单字节(0–255);处理文本应优先用rune,协议或二进制操作用byte;含非ASCII字符时切片/索引必须转[]rune,避免截断。
Go语言中,rune和byte看似都是整数类型,但语义完全不同:前者代表Unicode码点(即“字符”的逻辑单位),后者仅代表一个字节。搞混它们是字符串处理出错的常见原因。
byte 是 uint8 的别名,取值范围 0–255,它不关心编码,只负责存储原始字节数据。在 ASCII 范围内(0–127),一个 byte 往往对应一个可读字符;但一旦涉及中文、emoji 或带重音的字母(如 é),单个 byte 就无法表示完整字符了——因为 UTF-8 是变长编码。
len("你好") 返回 6(每个汉字占 3 字节)"你好"[0] 拿到的是第一个字节(0xe4),不是“你”这个字符for i := range s 中的 i 是字节偏移,不是字符位置rune 是 int32 的别名,用来明确表达“我这里存的是一个逻辑字符”。要得到字符串中的 rune 序列,必须显式转换:
runes := []rune("Hello 世界?") → 长度为 9(H/e/l/l/o/空格/世/界/?)for _, r := range "Hello 世界?" —— 这个 range 自动按 rune 解码,r 就是当前字符的码点fmt.Printf("%c %U\n", r, r) 可同时打印字符和 Unicode 编码(如 世 U+4E16)选型关键看操作对象:如果处理协议、文件头、网络包或做字节级校验,用 byte;如果
要做文本分析、截断、计数、大小写转换、正则匹配,必须用 rune。
string([]rune(s)[:3]),不能用 s[:3](可能截断汉字)len([]rune(s))
unicode.IsLetter(r)(参数是 rune)[]byte,比如 append([]byte("hello"), 0x00, 0xff)
很多 bug 来自把字符串当字节数组直接切片或索引。一个简单自查方法:只要你的字符串可能含中文、日文、emoji、áéíóú 或任何非 ASCII 字符,就别用 len(s) 或 s[i] 做“字符”相关计算。
s := "Go编程"; fmt.Println(s[2]) → 输出 177(字节),不是“编”的首字节r := []rune(s); fmt.Printf("%c", r[2]) → 输出 “编”[]rune 会拷贝整个字符串并解码,高频场景可考虑用 strings.Reader 或 utf8.DecodeRuneInString 流式处理基本上就这些。记住一句话:byte 是存储单位,rune 是语义单位。该解码的时候别省那一次转换。