Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

unicode/utf8 包详解

概述

unicode/utf8 包实现了支持 UTF-8 编码文本的函数和常量。它包括在 rune 和 UTF-8 字节序列之间转换的函数。

主要用途

  • UTF-8 编码和解码
  • rune 与字节序列转换
  • UTF-8 有效性验证
  • 计算 rune 数量和长度
  • 处理 UTF-8 字符串

核心概念

  • rune:Go 中的 Unicode 码点类型(int32 的别名)
  • UTF-8:变长字符编码,每个 rune 占用 1-4 个字节
  • 编码:将 rune 转换为 UTF-8 字节序列
  • 解码:将 UTF-8 字节序列转换为 rune

参考:https://en.wikipedia.org/wiki/UTF-8

包导入

import "unicode/utf8"

常量详解

基本编码常量

const (
    RuneError = '\uFFFD'      // 替换字符(用于无效 UTF-8)
    RuneSelf  = 0x80          // 自编码的 rune 范围上限
    MaxRune   = '\U0010FFFF'  // 最大 Unicode 码点
    UTFMax    = 4             // UTF-8 编码的最大字节数
)

说明

  • RuneError:替换字符,用于表示无效的 UTF-8 编码()
  • RuneSelf:小于此值的 rune 使用单字节编码(ASCII 兼容)
  • MaxRune:Unicode 标准定义的最大码点值
  • UTFMax:UTF-8 编码一个 rune 所需的最大字节数

示例

fmt.Printf("RuneError: %c (U+%04X)\n", unicode/utf8.RuneError, unicode/utf8.RuneError)
fmt.Printf("RuneSelf: 0x%X\n", unicode/utf8.RuneSelf)
fmt.Printf("MaxRune: U+%04X\n", unicode/utf8.MaxRune)
fmt.Printf("UTFMax: %d bytes\n", unicode/utf8.UTFMax)

// 输出:
// RuneError:  (U+FFFD)
// RuneSelf: 0x80
// MaxRune: U+10FFFF
// UTFMax: 4 bytes

函数详解(按 A-Z 分层归类)

A

AppendRune

func AppendRune(p []byte, r rune) []byte

作用:将 r 的 UTF-8 编码追加到 p 的末尾并返回扩展后的缓冲区

参数说明

  • p:目标字节切片
  • r:要编码的 rune

返回值

  • 扩展后的字节切片

说明

  • 如果 rune 超出范围,会追加 RuneError 的编码
  • p 不需要预先分配空间,会自动扩展

示例

// 基本用法
p := []byte("init")
p = utf8.AppendRune(p, '𐀀')  // U+10000
fmt.Println(string(p))
// 输出:init𐀀

// 追加多个 rune
var buf []byte
buf = utf8.AppendRune(buf, 'H')
buf = utf8.AppendRune(buf, 'i')
buf = utf8.AppendRune(buf, '!')
fmt.Println(string(buf))
// 输出:Hi!

// 处理无效 rune
buf = utf8.AppendRune(buf, unicode/utf8.MaxRune+1)
fmt.Println(string(buf))
// 输出:Hi! (追加了 RuneError)

D

DecodeLastRune

func DecodeLastRune(p []byte) (r rune, size int)

作用:解包 p 中最后一个 UTF-8 编码并返回 rune 及其字节宽度

参数说明

  • p:UTF-8 编码的字节切片

返回值

  • r:解码后的 rune
  • size:rune 的字节宽度

说明

  • 如果 p 为空,返回 (RuneError, 0)
  • 如果编码无效,返回 (RuneError, 1)
  • 无效编码包括:不正确的 UTF-8、超出范围的 rune、非最短编码

示例

// 解码字符串的最后一个字符
s := []byte("Hello, 世界")
for len(s) > 0 {
    r, size := utf8.DecodeLastRune(s)
    fmt.Printf("%c %d\n", r, size)
    s = s[:len(s)-size]
}
// 输出(反向):
// 界 3
// 世 3
//   1
// , 1
// o 1
// l 1
// l 1
// e 1
// H 1

// 处理空切片
r, size := utf8.DecodeLastRune([]byte{})
fmt.Printf("r=%c, size=%d\n", r, size)
// 输出:r=, size=0

// 处理无效 UTF-8
invalid := []byte{0xFF, 0xFE}
r, size = utf8.DecodeLastRune(invalid)
fmt.Printf("r=%c, size=%d\n", r, size)
// 输出:r=, size=1

DecodeLastRuneInString

func DecodeLastRuneInString(s string) (r rune, size int)

作用:与 DecodeLastRune 类似,但输入是字符串

参数说明

  • s:UTF-8 编码的字符串

返回值

  • r:解码后的 rune
  • size:rune 的字节宽度

说明

  • 如果 s 为空,返回 (RuneError, 0)
  • 如果编码无效,返回 (RuneError, 1)

示例

// 反向遍历字符串
str := "Hello, 世界"
for len(str) > 0 {
    r, size := utf8.DecodeLastRuneInString(str)
    fmt.Printf("%c %d\n", r, size)
    str = str[:len(str)-size]
}
// 输出(反向):
// 界 3
// 世 3
//   1
// , 1
// o 1
// l 1
// l 1
// e 1
// H 1

// 获取字符串的最后一个字符
last, size := utf8.DecodeLastRuneInString("你好")
fmt.Printf("Last char: %c, size: %d\n", last, size)
// 输出:Last char: 好,size: 3

DecodeRune

func DecodeRune(p []byte) (r rune, size int)

作用:解包 p 中第一个 UTF-8 编码并返回 rune 及其字节宽度

参数说明

  • p:UTF-8 编码的字节切片

返回值

  • r:解码后的 rune
  • size:rune 的字节宽度

说明

  • 如果 p 为空,返回 (RuneError, 0)
  • 如果编码无效,返回 (RuneError, 1)
  • 无效编码包括:不正确的 UTF-8、超出范围的 rune、非最短编码

示例

// 正向遍历字节切片
p := []byte("Hello, 世界")
for len(p) > 0 {
    r, size := utf8.DecodeRune(p)
    fmt.Printf("%c %d\n", r, size)
    p = p[size:]
}
// 输出:
// H 1
// e 1
// l 1
// l 1
// o 1
// , 1
//   1
// 世 3
// 界 3

// 处理空切片
r, size := utf8.DecodeRune([]byte{})
fmt.Printf("r=%c, size=%d\n", r, size)
// 输出:r=, size=0

// 处理无效 UTF-8
invalid := []byte{0x80, 0x81}
r, size = utf8.DecodeRune(invalid)
fmt.Printf("r=%c, size=%d\n", r, size)
// 输出:r=, size=1

DecodeRuneInString

func DecodeRuneInString(s string) (r rune, size int)

作用:与 DecodeRune 类似,但输入是字符串

参数说明

  • s:UTF-8 编码的字符串

返回值

  • r:解码后的 rune
  • size:rune 的字节宽度

说明

  • 如果 s 为空,返回 (RuneError, 0)
  • 如果编码无效,返回 (RuneError, 1)

示例

// 正向遍历字符串
str := "Hello, 世界"
for i := 0; i < len(str); {
    r, size := utf8.DecodeRuneInString(str[i:])
    fmt.Printf("%c %d\n", r, size)
    i += size
}
// 输出:
// H 1
// e 1
// l 1
// l 1
// o 1
// , 1
//   1
// 世 3
// 界 3

// 获取字符串的第一个字符
first, size := utf8.DecodeRuneInString("你好")
fmt.Printf("First char: %c, size: %d\n", first, size)
// 输出:First char: 你,size: 3

E

EncodeRune

func EncodeRune(p []byte, r rune) int

作用:将 r 的 UTF-8 编码写入 p(p 必须足够大)

参数说明

  • p:目标字节切片(必须至少为 UTFMax=4 字节)
  • r:要编码的 rune

返回值

  • 写入的字节数

说明

  • 如果 rune 超出范围,会写入 RuneError 的编码
  • p 必须足够大(至少 4 字节)

示例

// 编码单个 rune
p := make([]byte, utf8.UTFMax)
n := utf8.EncodeRune(p, '世')
fmt.Printf("%v\n", p[:n])
fmt.Printf("%d\n", n)
// 输出:
// [228 184 150]
// 3

// 编码多个 rune
var buf [utf8.UTFMax * 3]byte
offset := 0
offset += utf8.EncodeRune(buf[offset:], 'H')
offset += utf8.EncodeRune(buf[offset:], 'i')
offset += utf8.EncodeRune(buf[offset:], '!')
fmt.Println(string(buf[:offset]))
// 输出:Hi!

// 处理无效 rune(超出范围)
p = make([]byte, utf8.UTFMax)
n = utf8.EncodeRune(p, unicode/utf8.MaxRune+1)
fmt.Printf("%d: %v %c %d\n", 0, p[:n], p[:n], n)
// 输出:0: [239 191 189]  3

F

FullRune

func FullRune(p []byte) bool

作用:报告 p 中的字节是否以完整的 UTF-8 rune 编码开始

参数说明

  • p:UTF-8 编码的字节切片

返回值

  • 如果是完整的 rune 编码返回 true,否则返回 false

说明

  • 无效编码也被视为完整的 rune(因为会转换为宽度为 1 的错误 rune)

示例

// 完整的多字节 rune
p := []byte("世")  // 3 字节
fmt.Println(utf8.FullRune(p))
// 输出:true

// 不完整的 UTF-8 序列
p = []byte{0xE4, 0xB8}  // 只有 2 字节,需要 3 字节
fmt.Println(utf8.FullRune(p))
// 输出:false

// 完整的单字节 ASCII
p = []byte("A")
fmt.Println(utf8.FullRune(p))
// 输出:true

// 空切片
p = []byte{}
fmt.Println(utf8.FullRune(p))
// 输出:false

FullRuneInString

func FullRuneInString(s string) bool

作用:与 FullRune 类似,但输入是字符串

参数说明

  • s:UTF-8 编码的字符串

返回值

  • 如果是完整的 rune 编码返回 true,否则返回 false

示例

// 完整的字符串
fmt.Println(utf8.FullRuneInString("世"))
// 输出:true

// 不完整的 UTF-8 序列(通过字节转换)
s := string([]byte{0xE4, 0xB8})  // 不完整的 3 字节序列
fmt.Println(utf8.FullRuneInString(s))
// 输出:false

// 空字符串
fmt.Println(utf8.FullRuneInString(""))
// 输出:false

R

RuneCount

func RuneCount(p []byte) int

作用:返回 p 中的 rune 数量

参数说明

  • p:UTF-8 编码的字节切片

返回值

  • rune 数量

说明

  • 错误和短编码被视为单个 rune(宽度为 1 字节)

示例

// 计算 rune 数量
p := []byte("Hello, 世界")
fmt.Printf("bytes = %d\n", len(p))
fmt.Printf("runes = %d\n", utf8.RuneCount(p))
// 输出:
// bytes = 13
// runes = 9

// 空切片
fmt.Println(utf8.RuneCount([]byte{}))
// 输出:0

// 包含无效 UTF-8
invalid := []byte{0x80, 0x81, 'A'}
fmt.Println(utf8.RuneCount(invalid))
// 输出:3 (每个无效字节算作 1 个 rune)

RuneCountInString

func RuneCountInString(s string) int

作用:与 RuneCount 类似,但输入是字符串

参数说明

  • s:UTF-8 编码的字符串

返回值

  • rune 数量

示例

// 计算字符串的 rune 数量
s := "Hello, 世界"
fmt.Printf("bytes = %d\n", len(s))
fmt.Printf("runes = %d\n", utf8.RuneCountInString(s))
// 输出:
// bytes = 13
// runes = 9

// 空字符串
fmt.Println(utf8.RuneCountInString(""))
// 输出:0

// 只有 emoji
s = "😀😃😄"
fmt.Printf("bytes = %d\n", len(s))
fmt.Printf("runes = %d\n", utf8.RuneCountInString(s))
// 输出:bytes = 12, runes = 3

RuneLen

func RuneLen(r rune) int

作用:返回 rune 的 UTF-8 编码的字节数

参数说明

  • r:要计算的 rune

返回值

  • 字节数
  • 如果 rune 不是有效的 UTF-8 编码值,返回 -1

示例

// ASCII 字符
fmt.Println(utf8.RuneLen('A'))
// 输出:1

// 中文字符(通常在 U+0800 到 U+FFFF 之间)
fmt.Println(utf8.RuneLen('中'))
// 输出:3

// emoji(通常在 U+10000 以上)
fmt.Println(utf8.RuneLen('😀'))
// 输出:4

// 无效 rune(超出范围)
fmt.Println(utf8.RuneLen(unicode/utf8.MaxRune + 1))
// 输出:-1

// 代理对(无效)
fmt.Println(utf8.RuneLen(0xD800))
// 输出:-1

RuneStart

func RuneStart(b byte) bool

作用:报告字节是否可能是编码的(可能无效的)rune 的第一个字节

参数说明

  • b:要检查的字节

返回值

  • 如果可能是 rune 的第一个字节返回 true,否则返回 false

说明

  • 第二个及后续字节的前两位总是设置为 10
  • 第一个字节的前两位不是 10

示例

// ASCII 字符(0x00-0x7F)
fmt.Println(utf8.RuneStart('A'))  // 0x41
// 输出:true

// 多字节序列的第一个字节
fmt.Println(utf8.RuneStart(0xC0))  // 110xxxxx
// 输出:true
fmt.Println(utf8.RuneStart(0xE0))  // 1110xxxx
// 输出:true
fmt.Println(utf8.RuneStart(0xF0))  // 11110xxx
// 输出:true

//  continuation 字节(10xxxxxx)
fmt.Println(utf8.RuneStart(0x80))
// 输出:false
fmt.Println(utf8.RuneStart(0xBF))
// 输出:false

V

Valid

func Valid(p []byte) bool

作用:报告 p 是否完全由有效的 UTF-8 编码的 rune 组成

参数说明

  • p:要验证的字节切片

返回值

  • 如果完全有效返回 true,否则返回 false

示例

// 有效的 UTF-8
p := []byte("Hello, 世界")
fmt.Println(utf8.Valid(p))
// 输出:true

// 无效的 UTF-8
invalid := []byte{0xFF, 0xFE}
fmt.Println(utf8.Valid(invalid))
// 输出:false

// 不完整的序列
incomplete := []byte{0xE4, 0xB8}  // 缺少最后一个字节
fmt.Println(utf8.Valid(incomplete))
// 输出:false

// 空切片是有效的
fmt.Println(utf8.Valid([]byte{}))
// 输出:true

ValidRune

func ValidRune(r rune) bool

作用:报告 r 是否可以合法地编码为 UTF-8

参数说明

  • r:要检查的 rune

返回值

  • 如果可以合法编码返回 true,否则返回 false

说明

  • 超出范围的码点是非法的
  • 代理对的一半是非法的(U+D800 到 U+DFFF)

示例

// 有效的 rune
fmt.Println(utf8.ValidRune('A'))
// 输出:true
fmt.Println(utf8.ValidRune('中'))
// 输出:true
fmt.Println(utf8.ValidRune(unicode/utf8.MaxRune))
// 输出:true

// 无效的 rune(超出范围)
fmt.Println(utf8.ValidRune(unicode/utf8.MaxRune + 1))
// 输出:false

// 无效的 rune(代理对)
fmt.Println(utf8.ValidRune(0xD800))  // 高代理
// 输出:false
fmt.Println(utf8.ValidRune(0xDFFF))  // 低代理
// 输出:false

ValidString

func ValidString(s string) bool

作用:报告 s 是否完全由有效的 UTF-8 编码的 rune 组成

参数说明

  • s:要验证的字符串

返回值

  • 如果完全有效返回 true,否则返回 false

示例

// 有效的 UTF-8 字符串
fmt.Println(utf8.ValidString("Hello, 世界"))
// 输出:true

// 通过无效字节构造的字符串
invalid := string([]byte{0xFF, 0xFE})
fmt.Println(utf8.ValidString(invalid))
// 输出:false

// 空字符串是有效的
fmt.Println(utf8.ValidString(""))
// 输出:true

类型详解

unicode/utf8 包不导出任何类型,所有功能通过函数提供。

典型示例

1. 遍历 UTF-8 字符串

package main

import (
    "fmt"
    "unicode/utf8"
)

func main() {
    str := "Hello, 世界!"
    
    // 方法 1:使用 range(推荐)
    fmt.Println("Using range:")
    for i, r := range str {
        fmt.Printf("Index %d: %c (U+%04X)\n", i, r, r)
    }
    
    // 方法 2:使用 DecodeRuneInString
    fmt.Println("\nUsing DecodeRuneInString:")
    for i := 0; i < len(str); {
        r, size := utf8.DecodeRuneInString(str[i:])
        fmt.Printf("Index %d: %c (size=%d)\n", i, r, size)
        i += size
    }
}

2. 反向遍历字符串

package main

import (
    "fmt"
    "unicode/utf8"
)

func reverseString(s string) string {
    runes := make([]rune, 0, utf8.RuneCountInString(s))
    
    for len(s) > 0 {
        r, size := utf8.DecodeLastRuneInString(s)
        runes = append(runes, r)
        s = s[:len(s)-size]
    }
    
    return string(runes)
}

func main() {
    str := "Hello, 世界!"
    reversed := reverseString(str)
    fmt.Printf("Original: %s\n", str)
    fmt.Printf("Reversed: %s\n", reversed)
    // 输出:Original: Hello, 世界!
    //      Reversed: !界世,olleH
}

3. 计算字符串的字节数和 rune 数

package main

import (
    "fmt"
    "unicode/utf8"
)

func main() {
    strings := []string{
        "Hello",
        "世界",
        "😀😃😄",
        "Hello, 世界!",
    }
    
    for _, s := range strings {
        fmt.Printf("%q:\n", s)
        fmt.Printf("  Bytes: %d\n", len(s))
        fmt.Printf("  Runes: %d\n", utf8.RuneCountInString(s))
        fmt.Printf("  Avg bytes per rune: %.2f\n", 
            float64(len(s))/float64(utf8.RuneCountInString(s)))
        fmt.Println()
    }
}

4. 验证 UTF-8 编码

package main

import (
    "fmt"
    "unicode/utf8"
)

func validateUTF8(data []byte) error {
    if !utf8.Valid(data) {
        return fmt.Errorf("invalid UTF-8 encoding")
    }
    return nil
}

func main() {
    // 有效的 UTF-8
    valid := []byte("Hello, 世界")
    if err := validateUTF8(valid); err != nil {
        fmt.Printf("Error: %v\n", err)
    } else {
        fmt.Println("Valid UTF-8")
    }
    
    // 无效的 UTF-8
    invalid := []byte{0xFF, 0xFE, 0xFD}
    if err := validateUTF8(invalid); err != nil {
        fmt.Printf("Error: %v\n", err)
    } else {
        fmt.Println("Valid UTF-8")
    }
}

5. 安全地截取字符串

package main

import (
    "fmt"
    "unicode/utf8"
)

// 安全地截取字符串,确保不截断多字节字符
func safeSubstring(s string, start, length int) string {
    if start >= len(s) {
        return ""
    }
    
    end := start + length
    if end > len(s) {
        end = len(s)
    }
    
    // 确保不在多字节字符中间截断
    for end < len(s) && !utf8.RuneStart(s[end]) {
        end++
    }
    
    return s[start:end]
}

func main() {
    s := "Hello, 世界!"
    
    // 正常截取
    fmt.Println(safeSubstring(s, 0, 5))  // Hello
    
    // 可能截断多字节字符
    fmt.Println(safeSubstring(s, 7, 2))  // 世界(自动调整)
    
    // 从中间开始
    fmt.Println(safeSubstring(s, 7, 10)) // 世界!
}

6. 编码和解码 rune

package main

import (
    "fmt"
    "unicode/utf8"
)

func main() {
    runes := []rune{'A', '中', '😀', '𐀀'}
    
    for _, r := range runes {
        // 编码
        buf := make([]byte, utf8.UTFMax)
        n := utf8.EncodeRune(buf, r)
        
        // 解码
        decoded, _ := utf8.DecodeRune(buf[:n])
        
        fmt.Printf("Rune: %c (U+%04X)\n", r, r)
        fmt.Printf("  Encoded: %v (%d bytes)\n", buf[:n], n)
        fmt.Printf("  Decoded: %c\n", decoded)
        fmt.Printf("  Match: %v\n\n", r == decoded)
    }
}

7. 使用 AppendRune 构建字符串

package main

import (
    "fmt"
    "unicode/utf8"
)

func main() {
    // 高效地构建 UTF-8 字符串
    var buf []byte
    
    runes := []rune{'H', 'e', 'l', 'l', 'o', ',', ' ', '世', '界', '!'}
    
    for _, r := range runes {
        buf = utf8.AppendRune(buf, r)
    }
    
    fmt.Println(string(buf))
    // 输出:Hello, 世界!
    
    // 与 strings.Builder 比较
    fmt.Printf("Bytes: %d\n", len(buf))
    fmt.Printf("Runes: %d\n", utf8.RuneCount(buf))
}

8. 检查 rune 长度

package main

import (
    "fmt"
    "unicode/utf8"
)

func main() {
    testRunes := []rune{
        'A',           // ASCII
        'é',           // Latin-1
        '中',           // CJK
        '😀',           // Emoji
        '𐀀',           // Supplementary
        0xD800,        // Invalid (surrogate)
        utf8.MaxRune + 1, // Invalid (out of range)
    }
    
    for _, r := range testRunes {
        length := utf8.RuneLen(r)
        fmt.Printf("%c (U+%04X): %d bytes\n", r, r, length)
    }
}

9. 处理不完整的 UTF-8 序列

package main

import (
    "fmt"
    "unicode/utf8"
)

func main() {
    // 完整的 UTF-8 序列
    complete := []byte("中")  // 3 字节
    fmt.Printf("Complete: %v\n", utf8.FullRune(complete))
    
    // 不完整的 UTF-8 序列
    incomplete1 := []byte{0xE4}        // 只有 1 字节
    incomplete2 := []byte{0xE4, 0xB8}  // 只有 2 字节
    
    fmt.Printf("Incomplete 1 byte: %v\n", utf8.FullRune(incomplete1))
    fmt.Printf("Incomplete 2 bytes: %v\n", utf8.FullRune(incomplete2))
    
    // 处理流式数据
    data := []byte{0xE4, 0xB8}  // 不完整的"中"
    fmt.Printf("Has full rune: %v\n", utf8.FullRune(data))
    
    // 添加缺失的字节
    data = append(data, 0xAD)
    fmt.Printf("After adding byte: %v\n", utf8.FullRune(data))
    
    // 解码
    r, size := utf8.DecodeRune(data)
    fmt.Printf("Decoded: %c (size=%d)\n", r, size)
}

10. 统计不同类型字符的数量

package main

import (
    "fmt"
    "unicode"
    "unicode/utf8"
)

func main() {
    str := "Hello, 世界!123"
    
    var letters, digits, spaces, punctuation, others int
    
    for i := 0; i < len(str); {
        r, size := utf8.DecodeRuneInString(str[i:])
        i += size
        
        switch {
        case unicode.IsLetter(r):
            letters++
        case unicode.IsDigit(r):
            digits++
        case unicode.IsSpace(r):
            spaces++
        case unicode.IsPunct(r):
            punctuation++
        default:
            others++
        }
    }
    
    fmt.Printf("String: %q\n", str)
    fmt.Printf("Bytes: %d, Runes: %d\n", len(str), utf8.RuneCountInString(str))
    fmt.Printf("Letters: %d\n", letters)
    fmt.Printf("Digits: %d\n", digits)
    fmt.Printf("Spaces: %d\n", spaces)
    fmt.Printf("Punctuation: %d\n", punctuation)
    fmt.Printf("Others: %d\n", others)
}

11. 转换大小写

package main

import (
    "fmt"
    "unicode"
    "unicode/utf8"
)

func toUpper(s string) string {
    buf := make([]byte, 0, len(s))
    
    for i := 0; i < len(s); {
        r, size := utf8.DecodeRuneInString(s[i:])
        i += size
        
        upper := unicode.ToUpper(r)
        buf = utf8.AppendRune(buf, upper)
    }
    
    return string(buf)
}

func main() {
    str := "hello, 世界!"
    upper := toUpper(str)
    
    fmt.Printf("Original: %s\n", str)
    fmt.Printf("Uppercase: %s\n", upper)
    // 输出:Original: hello, 世界!
    //      Uppercase: HELLO, 世界!
}

12. 查找 rune 在字符串中的位置

package main

import (
    "fmt"
    "unicode/utf8"
)

func indexRune(s string, target rune) int {
    for i := 0; i < len(s); {
        r, size := utf8.DecodeRuneInString(s[i:])
        if r == target {
            return i
        }
        i += size
    }
    return -1
}

func main() {
    str := "Hello, 世界!"
    
    pos1 := indexRune(str, 'H')
    pos2 := indexRune(str, '世')
    pos3 := indexRune(str, '!')
    pos4 := indexRune(str, 'X')
    
    fmt.Printf("Position of 'H': %d\n", pos1)
    fmt.Printf("Position of '世': %d\n", pos2)
    fmt.Printf("Position of '!': %d\n", pos3)
    fmt.Printf("Position of 'X': %d\n", pos4)
}

最佳实践

1. 使用 range 遍历字符串

// 推荐:使用 range
for i, r := range str {
    // i 是字节索引,r 是 rune
}

// 不推荐:直接索引
for i := 0; i < len(str); i++ {
    ch := str[i]  // 获取的是字节,不是 rune
}

2. 使用 RuneCountInString 获取 rune 数量

// 推荐
count := utf8.RuneCountInString(s)

// 不推荐(效率低)
count := len([]rune(s))

3. 验证 UTF-8 编码

// 在处理外部数据时验证
if !utf8.Valid(data) {
    return fmt.Errorf("invalid UTF-8")
}

// 或使用 ValidString
if !utf8.ValidString(s) {
    return fmt.Errorf("invalid UTF-8 string")
}

4. 使用 AppendRune 高效构建

// 推荐:使用 AppendRune
var buf []byte
for _, r := range runes {
    buf = utf8.AppendRune(buf, r)
}

// 或使用 strings.Builder
var builder strings.Builder
for _, r := range runes {
    builder.WriteRune(r)
}

5. 检查 rune 起始字节

// 在流式处理中检查完整 rune
if utf8.RuneStart(b) {
    // 是新 rune 的开始
} else {
    // 是 continuation 字节
}

6. 处理不完整的 UTF-8

// 在读取流式数据时
for len(data) > 0 {
    if !utf8.FullRune(data) {
        // 等待更多数据
        break
    }
    r, size := utf8.DecodeRune(data)
    // 处理 r
    data = data[size:]
}

与其他包配合

unicode 包

import (
    "unicode"
    "unicode/utf8"
)

// 结合使用进行字符处理
for i := 0; i < len(s); {
    r, size := utf8.DecodeRuneInString(s[i:])
    i += size
    
    if unicode.IsLetter(r) {
        // 处理字母
    }
}

strings 包

import (
    "strings"
    "unicode/utf8"
)

// 使用 strings.Builder 高效构建
var builder strings.Builder
builder.Grow(utf8.UTFMax * len(runes))

for _, r := range runes {
    builder.WriteRune(r)
}
result := builder.String()

bytes 包

import (
    "bytes"
    "unicode/utf8"
)

// 使用 bytes.Buffer
var buf bytes.Buffer
buf.Grow(utf8.UTFMax * 10)

utf8.EncodeRune(buf.Bytes(), 'A')

bufio 包

import (
    "bufio"
    "unicode/utf8"
)

// 读取 UTF-8 文本
reader := bufio.NewReader(file)
for {
    r, _, err := reader.ReadRune()
    if err != nil {
        break
    }
    // 处理 r
}

注意事项

1. len() 返回字节数,不是 rune 数

s := "你好"
fmt.Println(len(s))              // 6 (字节数)
fmt.Println(utf8.RuneCountInString(s)) // 2 (rune 数)

2. 字符串索引访问的是字节

s := "你好"
fmt.Println(s[0])    // 228 (字节值,不是 '你')
fmt.Println(s[1])    // 189
// 正确访问第一个字符
r, _ := utf8.DecodeRuneInString(s)
fmt.Println(r)       // 你

3. 无效 UTF-8 的处理

// DecodeRune 返回 (RuneError, 1) 表示无效
invalid := []byte{0xFF, 0xFE}
r, size := utf8.DecodeRune(invalid)
fmt.Printf("r=%c, size=%d\n", r, size)
// 输出:r=, size=1

4. 代理对是无效的

// UTF-8 不允许代理对
fmt.Println(utf8.ValidRune(0xD800))  // false
fmt.Println(utf8.ValidRune(0xDFFF))  // false

5. 非最短编码是无效的

// 使用非最短编码会被拒绝
// 例如:用 2 字节编码 ASCII 字符
invalid := []byte{0xC0, 0x80}  // 试图编码 NUL
r, size := utf8.DecodeRune(invalid)
fmt.Printf("r=%c, size=%d\n", r, size)
// 输出:r=, size=1

6. 空输入的处理

// 空输入返回特殊值
r, size := utf8.DecodeRune([]byte{})
fmt.Printf("r=%c, size=%d\n", r, size)
// 输出:r=, size=0

fmt.Println(utf8.FullRune([]byte{}))  // false
fmt.Println(utf8.Valid([]byte{}))     // true

7. EncodeRune 需要足够的空间

// p 必须至少为 UTFMax (4) 字节
p := make([]byte, utf8.UTFMax)
n := utf8.EncodeRune(p, 'A')

// 如果空间不足会 panic
// p := make([]byte, 1)
// utf8.EncodeRune(p, '中')  // panic!

快速参考

常量速查表

常量说明
RuneErrorU+FFFD替换字符
RuneSelf0x80自编码范围上限
MaxRuneU+10FFFF最大 Unicode 码点
UTFMax4最大字节数

函数速查表

函数说明
AppendRune追加 rune 编码到字节切片
DecodeLastRune解码最后一个 rune
DecodeLastRuneInString解码字符串的最后一个 rune
DecodeRune解码第一个 rune
DecodeRuneInString解码字符串的第一个 rune
EncodeRune编码 rune 到字节切片
FullRune检查是否有完整 rune
FullRuneInString检查字符串是否有完整 rune
RuneCount计算 rune 数量
RuneCountInString计算字符串的 rune 数量
RuneLen获取 rune 的编码长度
RuneStart检查是否是 rune 起始字节
Valid验证 UTF-8 编码
ValidRune验证 rune 是否可编码
ValidString验证 UTF-8 字符串

UTF-8 编码规则

Unicode 范围UTF-8 编码字节数
U+0000 - U+007F0xxxxxxx1
U+0080 - U+07FF110xxxxx 10xxxxxx2
U+0800 - U+FFFF1110xxxx 10xxxxxx 10xxxxxx3
U+10000 - U+10FFFF11110xxx 10xxxxxx 10xxxxxx 10xxxxxx4

常见模式

// 遍历字符串
for i, r := range str {
    // i 是字节索引,r 是 rune
}

// 计算 rune 数量
count := utf8.RuneCountInString(s)

// 获取第一个字符
r, size := utf8.DecodeRuneInString(s)

// 获取最后一个字符
r, size := utf8.DecodeLastRuneInString(s)

// 验证 UTF-8
if !utf8.Valid(data) {
    // 处理错误
}

// 编码 rune
buf := make([]byte, utf8.UTFMax)
n := utf8.EncodeRune(buf, r)

// 追加 rune
buf = utf8.AppendRune(buf, r)

// 检查完整 rune
if utf8.FullRune(data) {
    // 有完整的 rune
}

总结

unicode/utf8 包提供了完整的 UTF-8 编码支持功能:

核心功能

  • rune 与 UTF-8 字节序列的相互转换
  • UTF-8 编码验证
  • rune 数量和长度计算
  • 完整的和不完整的 UTF-8 序列处理

主要函数

  • 编码:EncodeRuneAppendRune
  • 解码:DecodeRuneDecodeLastRuneDecodeRuneInStringDecodeLastRuneInString
  • 验证:ValidValidStringValidRune
  • 计算:RuneCountRuneCountInStringRuneLen
  • 检查:FullRuneFullRuneInStringRuneStart

常量

  • RuneError:替换字符
  • RuneSelf:自编码范围上限
  • MaxRune:最大 Unicode 码点
  • UTFMax:最大字节数(4)

使用建议

  1. 使用 range 遍历字符串
  2. 使用 RuneCountInString 获取 rune 数量
  3. 验证外部数据的 UTF-8 编码
  4. 使用 AppendRune 高效构建字符串
  5. 注意 len() 返回的是字节数
  6. 处理流式数据时检查完整 rune

典型用法

// 遍历字符串
for i, r := range str {
    fmt.Printf("%c (U+%04X) at byte %d\n", r, r, i)
}

// 编码和解码
buf := make([]byte, utf8.UTFMax)
n := utf8.EncodeRune(buf, '中')
r, _ := utf8.DecodeRune(buf[:n])

// 验证
if !utf8.ValidString(s) {
    // 处理无效 UTF-8
}

通过 unicode/utf8 包,可以方便地处理 UTF-8 编码的文本,支持国际化应用程序的开发。