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

encoding/base32 - Base32 编解码

概述

encoding/base32 包提供了 Base32 编码和解码功能。

Base32 是什么

  • 📦 二进制到文本编码:将二进制数据转换为可打印 ASCII 文本
  • 🔧 RFC 4648 标准:遵循互联网标准规范
  • 📋 32 个字符集:使用 A-Z 和 2-7(共 32 个字符)
  • 🛠️ 不区分大小写:解码时忽略大小写

主要用途

  • 📁 文件名编码:适合文件系统的命名(不区分大小写)
  • 🗣️ 口头传输:字符集简单,适合语音传达
  • 🔐 密钥编码:TOTP/HOTP 密钥常用 Base32 编码
  • 📧 邮件附件:某些邮件系统使用 Base32
  • 🏷️ 标识符生成:生成人类可读的唯一标识符

重要说明

  • ⚠️ 空间效率:编码后数据增加约 60%(相比原始数据)
  • ⚠️ 不区分大小写:编码输出大写,解码接受大小写
  • ⚠️ 填充字符:使用 = 作为填充
  • 标准库支持:Go 标准库提供完整支持
  • 流式处理:支持 Encoder/Decoder 流式编解码

与 Base64 的比较

特性Base32Base64
字符集大小3264
字符集A-Z, 2-7A-Z, a-z, 0-9, +, /
空间效率+60%+33%
大小写敏感
适用场景文件名、口头通用

Base32 编码原理

编码算法

基本步骤

  1. 将输入数据按 5 字节(40 位)分组
  2. 将 40 位数据分成 8 个 5 位组
  3. 每个 5 位组映射到一个 Base32 字符(0-31)
  4. 如果最后不足 5 字节,使用 = 填充

编码效率

5 字节二进制数据 = 40 位
8 个 Base32 字符 = 8 × 5 位 = 40 位
空间效率:8/5 = 1.6(增加 60%)

字符集

标准 Base32 字符集(RFC 4648)

ABCDEFGHIJKLMNOPQRSTUVWXYZ234567
01234567890123456789012345678901

字符映射

值 0-25  → A-Z
值 26-31 → 2-7

填充规则

输入字节数 | 输出字符数 | 填充数
----------|-----------|-------
    1     |     8     |   6 (=)
    2     |     8     |   4 (=)
    3     |     8     |   3 (=)
    4     |     8     |   2 (=)
    5     |     8     |   1 (=)
    5n    |    8n     |   0

核心类型

1. Encoding - 编码器

type Encoding struct {
    // 包含过滤或未导出的字段
}

功能:表示一个 Base32 编码器配置。

预定义编码器

var (
    StdEncoding  *Encoding  // 标准 Base32(RFC 4648)
    HexEncoding  *Encoding  // Base32hex(RFC 4648)
)

主要方法

// 编码
func (enc *Encoding) Encode(dst, src []byte)
func (enc *Encoding) EncodeToString(src []byte) string

// 解码
func (enc *Encoding) Decode(dst, src []byte) (n int, err error)
func (enc *Encoding) DecodeString(s string) ([]byte, error)

// 长度计算
func (enc *Encoding) EncodedLen(n int) int
func (enc *Encoding) DecodedLen(n int) int

// 流式编解码
func (enc *Encoding) NewEncoder(w io.Writer) *Encoder
func (enc *Encoding) NewDecoder(r io.Reader) *Decoder

// 验证
func (enc *Encoding) Strict() *Encoding

2. Encoder - 编码流

type Encoder struct {
    // 包含过滤或未导出的字段
}

功能:将数据流式编码为 Base32。

主要方法

// 写入数据
func (e *Encoder) Write(p []byte) (n int, err error)

// 关闭编码器(写入填充)
func (e *Encoder) Close() error

使用示例

var buf strings.Builder
encoder := base32.StdEncoding.NewEncoder(&buf)
encoder.Write([]byte("Hello"))
encoder.Close()
fmt.Println(buf.String())

3. Decoder - 解码流

type Decoder struct {
    // 包含过滤或未导出的字段
}

功能:从 Base32 数据流式解码。

主要方法

// 读取数据
func (d *Decoder) Read(p []byte) (n int, err error)

使用示例

decoder := base32.StdEncoding.NewDecoder(strings.NewReader("JBSWY3DP"))
data := make([]byte, 100)
n, err := decoder.Read(data)

核心函数

基本编解码

// 编码到字符串
func StdEncoding.EncodeToString(src []byte) string

// 从字符串解码
func StdEncoding.DecodeString(s string) ([]byte, error)

// 编码到缓冲区
func StdEncoding.Encode(dst, src []byte)

// 从缓冲区解码
func StdEncoding.Decode(dst, src []byte) (int, error)

// 长度计算
func StdEncoding.EncodedLen(n int) int
func StdEncoding.DecodedLen(n int) int

完整示例

示例 1:基本编解码

package main

import (
    "encoding/base32"
    "fmt"
    "log"
)

func main() {
    // 原始数据
    data := []byte("Hello, World!")
    fmt.Printf("原始数据:%s\n", string(data))
    fmt.Printf("原始长度:%d 字节\n\n", len(data))
    
    // 编码
    encoded := base32.StdEncoding.EncodeToString(data)
    fmt.Printf("Base32 编码:%s\n", encoded)
    fmt.Printf("编码长度:%d 字符\n\n", len(encoded))
    
    // 解码
    decoded, err := base32.StdEncoding.DecodeString(encoded)
    if err != nil {
        log.Fatalf("解码失败:%v", err)
    }
    fmt.Printf("Base32 解码:%s\n", string(decoded))
    fmt.Printf("解码长度:%d 字节\n", len(decoded))
    
    // 验证
    if string(decoded) == string(data) {
        fmt.Println("\n✓ 编解码成功!")
    }
}

输出

原始数据:Hello, World!
原始长度:13 字节

Base32 编码:JBSWY3DPEB3W64TMMQ======
编码长度:24 字符

Base32 解码:Hello, World!
解码长度:13 字节

✓ 编解码成功!

示例 2:不同编码器

package main

import (
    "encoding/base32"
    "fmt"
    "log"
)

func main() {
    data := []byte("Base32 Example")
    
    // 1. 标准 Base32
    stdEncoded := base32.StdEncoding.EncodeToString(data)
    fmt.Printf("标准 Base32: %s\n", stdEncoded)
    
    // 2. Base32hex(使用数字 0-9 和字母 A-V)
    hexEncoded := base32.HexEncoding.EncodeToString(data)
    fmt.Printf("Base32hex:    %s\n", hexEncoded)
    
    // 解码验证
    stdDecoded, _ := base32.StdEncoding.DecodeString(stdEncoded)
    hexDecoded, _ := base32.HexEncoding.DecodeString(hexEncoded)
    
    fmt.Printf("\n标准解码:%s\n", string(stdDecoded))
    fmt.Printf("Hex 解码:%s\n", string(hexDecoded))
    
    // 3. 长度对比
    fmt.Printf("\n长度对比:\n")
    fmt.Printf("原始:%d 字节\n", len(data))
    fmt.Printf("标准 Base32: %d 字符 (+%.0f%%)\n", 
        len(stdEncoded), 
        float64(len(stdEncoded)-len(data))/float64(len(data))*100)
    fmt.Printf("Base32hex: %d 字符 (+%.0f%%)\n", 
        len(hexEncoded), 
        float64(len(hexEncoded)-len(data))/float64(len(data))*100)
}

示例 3:流式编解码

package main

import (
    "encoding/base32"
    "fmt"
    "io"
    "log"
    "os"
    "strings"
)

func main() {
    // 1. 编码大文件
    fmt.Println("=== 编码大文件 ===")
    
    // 模拟大文件内容
    largeData := strings.Repeat("This is a large file content. ", 1000)
    
    var encoded strings.Builder
    encoder := base32.StdEncoding.NewEncoder(&encoded)
    
    // 分块写入
    chunkSize := 1024
    for i := 0; i < len(largeData); i += chunkSize {
        end := i + chunkSize
        if end > len(largeData) {
            end = len(largeData)
        }
        
        _, err := encoder.Write([]byte(largeData[i:end]))
        if err != nil {
            log.Fatal(err)
        }
    }
    
    encoder.Close()
    
    fmt.Printf("原始大小:%d 字节\n", len(largeData))
    fmt.Printf("编码大小:%d 字符\n", encoded.Len())
    fmt.Printf("编码前 100 字符:%s...\n\n", encoded.String()[:100])
    
    // 2. 解码
    fmt.Println("=== 解码 ===")
    
    decoder := base32.StdEncoding.NewDecoder(strings.NewReader(encoded.String()))
    
    var decoded strings.Builder
    buffer := make([]byte, 1024)
    
    for {
        n, err := decoder.Read(buffer)
        if n > 0 {
            decoded.Write(buffer[:n])
        }
        if err == io.EOF {
            break
        }
        if err != nil {
            log.Fatal(err)
        }
    }
    
    fmt.Printf("解码大小:%d 字节\n", decoded.Len())
    fmt.Printf("验证:%v\n\n", decoded.String() == largeData)
    
    // 3. 实际文件编码示例
    fmt.Println("=== 文件编码示例 ===")
    
    // 编码文件
    encodeFile := func(inputPath, outputPath string) error {
        inputFile, err := os.Open(inputPath)
        if err != nil {
            return err
        }
        defer inputFile.Close()
        
        outputFile, err := os.Create(outputPath)
        if err != nil {
            return err
        }
        defer outputFile.Close()
        
        encoder := base32.StdEncoding.NewEncoder(outputFile)
        buffer := make([]byte, 4096)
        
        for {
            n, err := inputFile.Read(buffer)
            if n > 0 {
                encoder.Write(buffer[:n])
            }
            if err == io.EOF {
                break
            }
            if err != nil {
                return err
            }
        }
        
        return encoder.Close()
    }
    
    // 解码文件
    decodeFile := func(inputPath, outputPath string) error {
        inputFile, err := os.Open(inputPath)
        if err != nil {
            return err
        }
        defer inputFile.Close()
        
        outputFile, err := os.Create(outputPath)
        if err != nil {
            return err
        }
        defer outputFile.Close()
        
        decoder := base32.StdEncoding.NewDecoder(inputFile)
        buffer := make([]byte, 4096)
        
        for {
            n, err := decoder.Read(buffer)
            if n > 0 {
                outputFile.Write(buffer[:n])
            }
            if err == io.EOF {
                break
            }
            if err != nil {
                return err
            }
        }
        
        return nil
    }
    
    fmt.Println("文件编解码函数已定义")
    fmt.Println("使用方法:")
    fmt.Println("  encodeFile(\"input.bin\", \"output.b32\")")
    fmt.Println("  decodeFile(\"output.b32\", \"restored.bin\")")
}

示例 4:TOTP 密钥编码

package main

import (
    "crypto/rand"
    "encoding/base32"
    "fmt"
    "log"
    "strings"
)

// GenerateTOTPSecret 生成 TOTP 密钥
func GenerateTOTPSecret(length int) (string, error) {
    if length <= 0 {
        length = 20 // 默认 20 字节(160 位)
    }
    
    // 生成随机字节
    secret := make([]byte, length)
    _, err := rand.Read(secret)
    if err != nil {
        return "", err
    }
    
    // Base32 编码
    encoded := base32.StdEncoding.EncodeToString(secret)
    
    // 格式化(每 4 个字符一组)
    var formatted strings.Builder
    for i := 0; i < len(encoded); i += 4 {
        if i > 0 {
            formatted.WriteString(" ")
        }
        end := i + 4
        if end > len(encoded) {
            end = len(encoded)
        }
        formatted.WriteString(encoded[i:end])
    }
    
    return formatted.String(), nil
}

// ValidateTOTPSecret 验证 TOTP 密钥格式
func ValidateTOTPSecret(secret string) bool {
    // 移除空格
    secret = strings.ReplaceAll(secret, " ", "")
    
    // 验证字符集(只包含 A-Z 和 2-7)
    validChars := "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567="
    for _, c := range secret {
        found := false
        for _, valid := range validChars {
            if c == valid {
                found = true
                break
            }
        }
        if !found {
            return false
        }
    }
    
    return true
}

// DecodeTOTPSecret 解码 TOTP 密钥
func DecodeTOTPSecret(secret string) ([]byte, error) {
    // 移除空格和连字符
    secret = strings.ReplaceAll(secret, " ", "")
    secret = strings.ReplaceAll(secret, "-", "")
    
    // 转为大写
    secret = strings.ToUpper(secret)
    
    // 添加填充(如果需要)
    for len(secret)%8 != 0 {
        secret += "="
    }
    
    // 解码
    return base32.StdEncoding.DecodeString(secret)
}

func main() {
    fmt.Println("=== TOTP 密钥生成器 ===\n")
    
    // 生成多个密钥
    for i := 1; i <= 5; i++ {
        secret, err := GenerateTOTPSecret(20)
        if err != nil {
            log.Fatal(err)
        }
        
        fmt.Printf("密钥 %d: %s\n", i, secret)
        
        // 验证
        if ValidateTOTPSecret(secret) {
            fmt.Println("  ✓ 格式有效")
        } else {
            fmt.Println("  ✗ 格式无效")
        }
        
        // 解码验证
        decoded, err := DecodeTOTPSecret(secret)
        if err != nil {
            fmt.Printf("  ✗ 解码失败:%v\n", err)
        } else {
            fmt.Printf("  ✓ 解码成功:%d 字节\n", len(decoded))
        }
        
        fmt.Println()
    }
    
    // 示例:用户输入验证
    fmt.Println("=== 用户输入示例 ===")
    userInput := "JBSW Y3DP EB3W 64TM"
    fmt.Printf("用户输入:%s\n", userInput)
    
    if ValidateTOTPSecret(userInput) {
        fmt.Println("✓ 格式有效")
        
        decoded, _ := DecodeTOTPSecret(userInput)
        fmt.Printf("✓ 解码:%x (%d 字节)\n", decoded, len(decoded))
    }
}

示例 5:文件名安全编码

package main

import (
    "crypto/sha256"
    "encoding/base32"
    "fmt"
    "log"
    "os"
    "path/filepath"
    "strings"
)

// GenerateSafeFilename 生成安全的文件名
func GenerateSafeFilename(originalName string) string {
    // 计算哈希
    hash := sha256.Sum256([]byte(originalName))
    
    // Base32 编码
    encoded := base32.StdEncoding.EncodeToString(hash[:])
    
    // 截断到合理长度(去掉填充)
    encoded = strings.TrimRight(encoded, "=")
    if len(encoded) > 32 {
        encoded = encoded[:32]
    }
    
    // 添加原始扩展名
    ext := filepath.Ext(originalName)
    if ext != "" {
        encoded += ext
    }
    
    return encoded
}

// SanitizeFilename 清理文件名(Base32 编码特殊字符)
func SanitizeFilename(filename string) string {
    // 检查是否需要编码
    needsEncoding := false
    for _, c := range filename {
        if c < 32 || c > 126 || strings.ContainsRune("<>:\"/\\|?*", c) {
            needsEncoding = true
            break
        }
    }
    
    if !needsEncoding {
        return filename
    }
    
    // Base32 编码
    encoded := base32.StdEncoding.EncodeToString([]byte(filename))
    encoded = strings.TrimRight(encoded, "=")
    
    return "enc_" + encoded
}

// DecodeFilename 解码文件名
func DecodeFilename(encoded string) (string, error) {
    if !strings.HasPrefix(encoded, "enc_") {
        return encoded, nil
    }
    
    encoded = strings.TrimPrefix(encoded, "enc_")
    
    // 添加填充
    for len(encoded)%8 != 0 {
        encoded += "="
    }
    
    return base32.StdEncoding.DecodeString(encoded)
}

// CreateUniqueFilename 创建唯一文件名
func CreateUniqueFilename(baseName string) string {
    // 使用时间戳和随机数
    timestamp := fmt.Sprintf("%d", os.Getpid())
    unique := baseName + "_" + timestamp
    
    return GenerateSafeFilename(unique)
}

func main() {
    fmt.Println("=== 文件名安全编码 ===\n")
    
    // 示例 1:哈希文件名
    fmt.Println("示例 1:哈希文件名")
    files := []string{
        "document.pdf",
        "photo.jpg",
        "报告.docx",
        "数据备份 2024.zip",
    }
    
    for _, file := range files {
        safeName := GenerateSafeFilename(file)
        fmt.Printf("  %s -> %s\n", file, safeName)
    }
    
    fmt.Println()
    
    // 示例 2:清理特殊字符
    fmt.Println("示例 2:清理特殊字符")
    specialFiles := []string{
        "file<1>.txt",
        "test|file.txt",
        "data?.csv",
        "file:name.txt",
    }
    
    for _, file := range specialFiles {
        safeName := SanitizeFilename(file)
        fmt.Printf("  %s -> %s\n", file, safeName)
        
        // 验证可解码
        decoded, err := DecodeFilename(safeName)
        if err != nil {
            fmt.Printf("    ✗ 解码失败:%v\n", err)
        } else {
            fmt.Printf("    ✓ 可解码:%s\n", decoded)
        }
    }
    
    fmt.Println()
    
    // 示例 3:唯一文件名
    fmt.Println("示例 3:唯一文件名")
    for i := 0; i < 3; i++ {
        unique := CreateUniqueFilename("backup")
        fmt.Printf("  唯一文件名:%s\n", unique)
    }
}

示例 6:自定义编码配置

package main

import (
    "encoding/base32"
    "fmt"
    "log"
)

func main() {
    data := []byte("Custom Base32 Test")
    
    // 1. 使用标准编码
    stdEncoded := base32.StdEncoding.EncodeToString(data)
    fmt.Printf("标准编码:%s\n", stdEncoded)
    
    // 2. 使用 Hex 编码
    hexEncoded := base32.HexEncoding.EncodeToString(data)
    fmt.Printf("Hex 编码:%s\n", hexEncoded)
    
    // 3. 长度计算
    fmt.Printf("\n长度计算:\n")
    fmt.Printf("原始数据:%d 字节\n", len(data))
    fmt.Printf("编码后:%d 字符\n", base32.StdEncoding.EncodedLen(len(data)))
    fmt.Printf("解码后:%d 字节\n", base32.StdEncoding.DecodedLen(len(stdEncoded)))
    
    // 4. 直接缓冲区操作
    fmt.Printf("\n缓冲区操作:\n")
    
    // 编码到预分配缓冲区
    encodedBuf := make([]byte, base32.StdEncoding.EncodedLen(len(data)))
    base32.StdEncoding.Encode(encodedBuf, data)
    fmt.Printf("编码缓冲区:%s\n", string(encodedBuf))
    
    // 从缓冲区解码
    decodedBuf := make([]byte, base32.StdEncoding.DecodedLen(len(encodedBuf)))
    n, err := base32.StdEncoding.Decode(decodedBuf, encodedBuf)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("解码缓冲区:%s (%d 字节)\n", string(decodedBuf[:n]), n)
    
    // 5. 验证编码
    fmt.Printf("\n验证:\n")
    fmt.Printf("标准 == 标准:%v\n", stdEncoded == string(encodedBuf))
    fmt.Printf("Hex != 标准:%v\n", hexEncoded != stdEncoded)
}

示例 7:错误处理

package main

import (
    "encoding/base32"
    "fmt"
    "log"
)

func main() {
    // 1. 有效的 Base32 字符串
    validCases := []string{
        "JBSWY3DP",
        "JBSWY3DPEA======",
        "MFRGGZDFMY======",
        "ORSXG5A=",
    }
    
    fmt.Println("=== 有效输入 ===")
    for _, s := range validCases {
        decoded, err := base32.StdEncoding.DecodeString(s)
        if err != nil {
            fmt.Printf("✗ %s -> 错误:%v\n", s, err)
        } else {
            fmt.Printf("✓ %s -> %x (%d 字节)\n", s, decoded, len(decoded))
        }
    }
    
    // 2. 无效的 Base32 字符串
    fmt.Println("\n=== 无效输入 ===")
    invalidCases := []string{
        "JBSW!3DP",    // 包含无效字符!
        "JBSWY3D8",    // 包含 8 和 9(不在字符集中)
        "jbswy3dp",    // 小写(某些实现可能不接受)
        "JBSWY3D",     // 长度错误(缺少填充)
        "JBSWY3DP====", // 填充错误
    }
    
    for _, s := range invalidCases {
        decoded, err := base32.StdEncoding.DecodeString(s)
        if err != nil {
            fmt.Printf("✗ %s -> 错误:%v\n", s, err)
        } else {
            fmt.Printf("? %s -> %x (可能已自动修正)\n", s, decoded)
        }
    }
    
    // 3. 大小写处理
    fmt.Println("\n=== 大小写处理 ===")
    cases := []string{
        "JBSWY3DP",
        "jbswy3dp",
        "JbSwY3Dp",
        "JBswY3dP",
    }
    
    for _, s := range cases {
        decoded, err := base32.StdEncoding.DecodeString(s)
        if err != nil {
            fmt.Printf("✗ %s -> 错误:%v\n", s, err)
        } else {
            fmt.Printf("✓ %s -> %s\n", s, string(decoded))
        }
    }
    
    // 4. 填充处理
    fmt.Println("\n=== 填充处理 ===")
    paddingCases := []struct {
        input       string
        description string
    }{
        {"JBSWY3DP", "无填充"},
        {"JBSWY3DP=", "1 个填充"},
        {"JBSWY3DPEA======", "正确填充"},
    }
    
    for _, tc := range paddingCases {
        decoded, err := base32.StdEncoding.DecodeString(tc.input)
        if err != nil {
            fmt.Printf("✗ %s (%s) -> 错误:%v\n", tc.input, tc.description, err)
        } else {
            fmt.Printf("✓ %s (%s) -> %s\n", tc.input, tc.description, string(decoded))
        }
    }
    
    // 5. 缓冲区大小错误
    fmt.Println("\n=== 缓冲区大小测试 ===")
    data := []byte("Test data")
    encoded := make([]byte, base32.StdEncoding.EncodedLen(len(data)))
    base32.StdEncoding.Encode(encoded, data)
    
    // 正确的缓冲区大小
    decoded := make([]byte, base32.StdEncoding.DecodedLen(len(encoded)))
    n, err := base32.StdEncoding.Decode(decoded, encoded)
    if err != nil {
        log.Printf("解码错误:%v", err)
    } else {
        fmt.Printf("✓ 正确缓冲区:%s (%d 字节)\n", string(decoded[:n]), n)
    }
    
    // 过小的缓冲区
    smallBuf := make([]byte, 2)
    n, err = base32.StdEncoding.Decode(smallBuf, encoded)
    if err != nil {
        fmt.Printf("✗ 缓冲区过小:%v\n", err)
    } else {
        fmt.Printf("? 部分解码:%d 字节\n", n)
    }
}

最佳实践

✅ 推荐做法

  1. 使用标准编码器

    // ✅ 推荐
    encoded := base32.StdEncoding.EncodeToString(data)
    
    // ❌ 不推荐:创建自定义编码器(除非必要)
    
  2. 处理用户输入

    // 转换为大写并移除空格
    secret := strings.ToUpper(strings.ReplaceAll(input, " ", ""))
    decoded, err := base32.StdEncoding.DecodeString(secret)
    
  3. 添加填充

    // 解码前确保有正确的填充
    for len(s)%8 != 0 {
        s += "="
    }
    
  4. 流式处理大文件

    encoder := base32.StdEncoding.NewEncoder(outputFile)
    defer encoder.Close()
    encoder.Write(largeData)
    

❌ 不安全做法

  1. 不要忽略错误

    // ❌ 错误
    decoded, _ := base32.StdEncoding.DecodeString(input)
    
    // ✅ 正确
    decoded, err := base32.StdEncoding.DecodeString(input)
    if err != nil {
        return err
    }
    
  2. 不要假设字符集

    // ❌ 错误:假设只包含大写字母
    if c >= 'A' && c <= 'Z' { ... }
    
    // ✅ 正确:使用标准库验证
    _, err := base32.StdEncoding.DecodeString(s)
    

性能优化

预分配缓冲区

// ✅ 推荐:预分配缓冲区
encoded := make([]byte, base32.StdEncoding.EncodedLen(len(data)))
base32.StdEncoding.Encode(encoded, data)

// ❌ 不推荐:动态增长
var encoded []byte
for _, b := range data {
    encoded = append(encoded, encodeByte(b))
}

批量处理

// ✅ 推荐:批量编码
chunks := splitData(data, chunkSize)
for _, chunk := range chunks {
    encoded := base32.StdEncoding.EncodeToString(chunk)
    write(encoded)
}

// ❌ 不推荐:逐字节编码
for _, b := range data {
    encoded := base32.StdEncoding.EncodeToString([]byte{b})
    write(encoded)
}

总结

核心类型

类型用途说明
Encoding编码器配置定义编码规则
Encoder编码流流式编码
Decoder解码流流式解码

预定义编码器

编码器字符集用途
StdEncodingA-Z, 2-7标准 Base32
HexEncoding0-9, A-VBase32hex

核心方法

方法用途返回值
EncodeToString编码为字符串string
DecodeString从字符串解码[]byte, error
Encode编码到缓冲区-
Decode从缓冲区解码int, error
EncodedLen计算编码长度int
DecodedLen计算解码长度int

使用场景

场景推荐方法说明
TOTP 密钥EncodeToString生成人类可读密钥
文件名EncodeToString生成安全文件名
大文件NewEncoder/NewDecoder流式处理
标识符EncodeToString生成唯一 ID

字符集对比

特性Base32Base64
字符数3264
大小写不敏感敏感
特殊字符+, /
填充字符==
空间效率+60%+33%

参考资料


最后更新:2026-04-03
Go 版本:Go 1.23+