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
}

StdEncoding 的输出是 R28=,最后的等号用于把编码结果补齐到完整的 4 字符分组;RawStdEncoding 输出 R28,只省略这个填充字符。输入长度恰好是 3 的倍数时,两者可能看起来完全一样,所以不要用单个“无等号”的样例判断编码器。
标准字母表和 Raw 变体不要混为一谈
Base64 里有两个经常被同时讨论、但职责不同的选择:一是使用哪套 64 字符字母表,二是是否使用填充。StdEncoding 与 RawStdEncoding 都属于标准字母表;后者可以理解为标准编码的 NoPadding 版本。
| 编码器 | 字母表 | 填充 | 常见约束 |
|---|---|---|---|
StdEncoding | 标准 Base64 | 保留 = | 对接明确要求标准填充的协议或已有实现 |
RawStdEncoding | 标准 Base64 | 不保留 | 字段约定无填充,或长度希望更短 |
URLEncoding | URL 安全字母表 | 保留 = | 需要 URL 查询参数或路径安全字符时 |
RawURLEncoding | URL 安全字母表 | 不保留 | 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
}

如果服务端返回带 = 的值,而客户端固定用 RawStdEncoding 解码,或者反过来,通常会得到格式错误。排查时先把原始传输值完整记录为安全的可复现样本,再确认两端的编码器名称、是否做过字符串裁剪,以及协议是否允许两种形式同时存在。
什么时候选 Std,什么时候选 Raw
可以把选择规则收敛成一张小表:
| 场景 | 推荐 | 原因 |
|---|---|---|
| 新协议没有特别限制 | StdEncoding | 标准形式更容易和默认实现互通 |
| 协议明确规定无填充 | RawStdEncoding | 直接表达无填充约定,避免手工裁剪 |
| 对接方只给出一个固定解码器 | 跟随对方 | 兼容性优先于本地输出长度 |
| 问题涉及 URL 字符 | 另选 URL 变体 | 填充策略和字母表是两个独立维度 |
最稳妥的工程做法是把编码器作为协议适配层的一部分,不在业务代码中到处直接调用全局变量;同时为带填充和无填充各保留一个明确的测试样例,覆盖输入长度不是 3 的倍数的情况。
常见问题
RawStdEncoding 会把 + 和 / 换掉吗?
不会。它只取消标准 Base64 的末尾填充,仍然使用标准字母表。如果还需要 URL 安全字符,应考虑 RawURLEncoding。
为什么我的测试里两个编码结果一样?
当输入字节数恰好是 3 的倍数时,标准编码不需要补 =,两者自然相同。测试时可以加入长度为 1、2、4 或 5 的输入。
能不能把等号删掉再用 StdEncoding 解码?
不建议。应该按协议直接使用对应的 Raw 解码器;手工删改会让格式约定散落在调用方,也可能使另一端无法判断真实编码形式。
-
151 收藏
-
435 收藏
-
412 收藏
-
334 收藏
-
185 收藏
-
163 收藏
-
370 收藏
-
149 收藏
-
137 收藏
-
354 收藏
-
325 收藏
-
432 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习