登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  Golang >  Go问答

Go Base64 标准编码和 RawStdEncoding 有什么区别

来源:17golang原创

时间:2026-11-11 00:16:59 482浏览 收藏

Go 的 base64.StdEncoding 和 base64.RawStdEncoding 使用同一套标准 Base64 字母表,真正的区别是末尾是否保留 = 填充。需要和已有协议、签名格式或其他语言实现互通时,先确认对方是否允许填充,再让编码端和解码端成对选择;不要把 Raw 误解成 URL 安全编码。

要点速览
  • StdEncoding 使用标准填充字符 =,输出长度通常是 4 的倍数。
  • RawStdEncoding 只取消填充,字母表仍是标准字母表,+ 和 / 的语义没有变化。
  • 协议两端必须共享同一个约定;字段只允许无填充时选 Raw,兼容 RFC 4648 标准形式时优先选 Std。

先看清两种编码真正差在哪里

把同一段字节交给两个编码器,最容易看出区别。下面的例子选择长度不是 3 的倍数的输入,这样标准编码会出现填充,而 Raw 变体会省略它:

package main

import (
    "encoding/base64"
    "fmt"
)

func main() {
    input := []byte("Go")

    // 两个编码器使用同一套标准 Base64 字母表,只比较填充策略。
    standard := base64.StdEncoding.EncodeToString(input)
    raw := base64.RawStdEncoding.EncodeToString(input)

    fmt.Println(standard) // R28=
    fmt.Println(raw)      // R28
}
Go StdEncoding 与 RawStdEncoding 对同一段字节保留或去掉等号填充的结构说明图
图1:Base64 标准编码与 RawStdEncoding 的填充差异说明图,不是运行截图。

StdEncoding 的输出是 R28=,最后的等号用于把编码结果补齐到完整的 4 字符分组;RawStdEncoding 输出 R28,只省略这个填充字符。输入长度恰好是 3 的倍数时,两者可能看起来完全一样,所以不要用单个“无等号”的样例判断编码器。

标准字母表和 Raw 变体不要混为一谈

Base64 里有两个经常被同时讨论、但职责不同的选择:一是使用哪套 64 字符字母表,二是是否使用填充。StdEncoding 与 RawStdEncoding 都属于标准字母表;后者可以理解为标准编码的 NoPadding 版本。

编码器字母表填充常见约束
StdEncoding标准 Base64保留 =对接明确要求标准填充的协议或已有实现
RawStdEncoding标准 Base64不保留字段约定无填充,或长度希望更短
URLEncodingURL 安全字母表保留 =需要 URL 查询参数或路径安全字符时
RawURLEncodingURL 安全字母表不保留URL 安全且无填充的字段

本文只讨论前两行。若问题是“Base64 放进 URL 后出现 +、/ 或转义问题”,应另看 URL 安全字母表,不要只把 StdEncoding 换成 RawStdEncoding。

按协议约束选择编码器

选择时先问三个问题:对方是否明确要求 =;字段是否有固定长度或长度预算;对接方能否使用同样的解码器。只要协议文档给出一种形式,就以协议为准,不要因为 Raw 输出更短就自行切换。

package main

import (
    "encoding/base64"
    "fmt"
)

func encodeToken(raw []byte, withoutPadding bool) string {
    // 无填充只能在协议允许时启用,否则会改变线上字段格式。
    enc := base64.StdEncoding
    if withoutPadding {
        enc = base64.RawStdEncoding
    }
    return enc.EncodeToString(raw)
}

func main() {
    token := []byte("user-42")
    fmt.Println(encodeToken(token, false)) // 标准形式,可能带 '='
    fmt.Println(encodeToken(token, true))  // Raw 形式,不带 '='
}

对于签名输入、缓存键和短令牌,编码结果本身常常会参与哈希或拼接。此时多一个 = 不只是显示差异,而是会改变待签名字符串;编码策略必须在协议层固定,并在文档中写清“标准填充”还是“无填充”。

编码端和解码端必须成对选择

编码端使用哪种形式,解码端就应该使用对应的 Encoding。不要先用字符串裁剪去掉等号,再交给标准解码器;也不要把收到的无填充字段无条件补等号,因为你可能会掩盖协议双方配置不一致的问题。

package main

import (
    "encoding/base64"
    "fmt"
)

func decodeToken(value string, withoutPadding bool) ([]byte, error) {
    // 解码器与编码器保持同一填充约定,错误交给调用方处理。
    enc := base64.StdEncoding
    if withoutPadding {
        enc = base64.RawStdEncoding
    }
    decoded, err := enc.DecodeString(value)
    if err != nil {
        return nil, fmt.Errorf("base64 解码失败: %w", err)
    }
    return decoded, nil
}
Go Base64 协议字段中编码端与解码端按相同填充约定配对的结构说明图
图2:编码端与解码端按填充约定配对的结构说明图,不是运行截图。

如果服务端返回带 = 的值,而客户端固定用 RawStdEncoding 解码,或者反过来,通常会得到格式错误。排查时先把原始传输值完整记录为安全的可复现样本,再确认两端的编码器名称、是否做过字符串裁剪,以及协议是否允许两种形式同时存在。

什么时候选 Std,什么时候选 Raw

可以把选择规则收敛成一张小表:

场景推荐原因
新协议没有特别限制StdEncoding标准形式更容易和默认实现互通
协议明确规定无填充RawStdEncoding直接表达无填充约定,避免手工裁剪
对接方只给出一个固定解码器跟随对方兼容性优先于本地输出长度
问题涉及 URL 字符另选 URL 变体填充策略和字母表是两个独立维度

最稳妥的工程做法是把编码器作为协议适配层的一部分,不在业务代码中到处直接调用全局变量;同时为带填充和无填充各保留一个明确的测试样例,覆盖输入长度不是 3 的倍数的情况。

常见问题

RawStdEncoding 会把 + 和 / 换掉吗?

不会。它只取消标准 Base64 的末尾填充,仍然使用标准字母表。如果还需要 URL 安全字符,应考虑 RawURLEncoding。

为什么我的测试里两个编码结果一样?

当输入字节数恰好是 3 的倍数时,标准编码不需要补 =,两者自然相同。测试时可以加入长度为 1、2、4 或 5 的输入。

能不能把等号删掉再用 StdEncoding 解码?

不建议。应该按协议直接使用对应的 Raw 解码器;手工删改会让格式约定散落在调用方,也可能使另一端无法判断真实编码形式。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>