1. 前言
Base64 是一种将二进制数据转换为可打印字符的编码方案,广泛应用于电子邮件、网页传输等场景。它通过将每 3 个字节的二进制数据编码为 4 个 ASCII 字符,确保数据在文本环境中可靠传输。之前都是用Python实现,很久没用C语言了,今天复习一下
(RFC 文档读起来像说明书,但是核心思路就两个词:分组 + 映射 )
2. 技术简介
Base64 编码遵循 RFC 4648 标准,核心原理如下:
-
将输入数据按 3 字节分组;
-
将每组 24 位拆分为 4 个 6 位片段;
-
将每个 6 位片段映射到 Base64 字符表;
-
不足 3 字节时补零并用
=填充
编码后的数据长度固定为原数据长度的 4/3 倍(向上取整)
3. 算法原理讲解
3.1 数据分组与补零
-
将输入数据分割为多个 3 字节组;
-
最后一组不足 3 字节时补零:
-
1 字节剩余:补 2 字节零 → 生成 2 个数据字符 + 2 个
= -
2 字节剩余:补 1 字节零 → 生成 3 个数据字符 + 1 个
=
-
(这个时候就有人要问“为什么一定是 3 → 4,而不是 2 → 3?”——因为 24 可被 6 整除)
3.2 位操作与字符映射
-
将 3 字节拼接为 24 位整数
-
依次取 6 位片段(从高位到低位):
-
第 1 个字符:取高 6 位(位 23‑18)
-
第 2 个字符:取中间 6 位(位 17‑12)
-
第 3 个字符:取次中间 6 位(位 11‑6)
-
第 4 个字符:取低 6 位(位 5‑0)
-
3.3 填充处理
根据实际数据长度决定填充字符数量:
数据长度 % 3 == 0 → 无填充
数据长度 % 3 == 1 → 补 2 个 =
数据长度 % 3 == 2 → 补 1 个 =
(要补充一下那两个 = 不是装可爱,是 MIME 时代遗留下来的对齐标记)
4. 代码结构解析
4.1 Base64 映射表
static const char B64_TABLE[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
-
包含 64 个标准字符,索引 0‑63 对应不同字符
-
+和/为最后两个字符
(有时候为了安全,URL‑Safe 版本把 + 换成 -,/ 换成 _)
4.2 核心编码函数
4.2.1 函数原型
char *base64_encode(const uint8_t *data, size_t len);
-
输入:二进制数据指针
data及其长度len -
输出:动态分配的 Base64 字符串(需调用者释放)
4.2.2 内存分配
size_t out_len = (len + 2) / 3 * 4;
char *out = (char *)malloc(out_len + 1);
-
+2再除以 3:实现向上取整 -
多申请 1 字节给
\0终止符
(malloc 失败一定要记得判空!)
4.2.3 主处理循环
while (i < len) {
int remaining = len - i;
int chunk = remaining >= 3 ? 3 : remaining;
uint32_t triple = 0;
triple |= data[i] << 16;
if (chunk > 1) triple |= data[i+1] << 8;
if (chunk > 2) triple |= data[i+2];
out[o++] = B64_TABLE[(triple >> 18) & 0x3F];
out[o++] = B64_TABLE[(triple >> 12) & 0x3F];
out[o++] = (chunk > 1) ? B64_TABLE[(triple >> 6) & 0x3F] : '=';
out[o++] = (chunk > 2) ? B64_TABLE[triple & 0x3F] : '=';
i += chunk;
}
-
24 位拼接:移位操作组合最多 3 字节
-
动态填充:根据
chunk决定=数 -
位掩码:
0x3F留下 6 位
4.2.4 终止符处理
out[out_len] = '\0';
- 保证返回值是合法 C 字符串
5. 关键代码解析
5.1 24 位数据拼接
triple |= data[i] << 16; // 首字节占高位
if (chunk > 1)
triple |= data[i+1] << 8; // 次字节居中
if (chunk > 2)
triple |= data[i+2]; // 末字节占低位
<<和|比起memcpy更秀位级操作
5.2 动态截断与填充
out[o++] = (chunk > 1) ? ... : '='; // 第三字符
out[o++] = (chunk > 2) ? ... : '='; // 第四字符
- 仅 1 字节:补
==;仅 2 字节:补=
5.3 位掩码操作
(triple >> 18) & 0x3F // 高 6 位
(triple >> 12) & 0x3F // 次高 6 位
0x3F=0011 1111,精准抠出 6 bit,这一步要是写& 63也行,看个人口味
7. 完整实现代码
#include <stdlib.h>
#include <stdint.h>
static const char B64_TABLE[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
char *base64_encode(const uint8_t *data, size_t len) {
size_t out_len = (len + 2) / 3 * 4;
char *out = (char *)malloc(out_len + 1);
if (!out) return NULL;
size_t i = 0, o = 0;
while (i < len) {
int remaining = len - i;
int chunk = remaining >= 3 ? 3 : remaining;
uint32_t triple = data[i] << 16;
if (chunk > 1) triple |= data[i + 1] << 8;
if (chunk > 2) triple |= data[i + 2];
out[o++] = B64_TABLE[(triple >> 18) & 0x3F];
out[o++] = B64_TABLE[(triple >> 12) & 0x3F];
out[o++] = (chunk > 1) ? B64_TABLE[(triple >> 6) & 0x3F] : '=';
out[o++] = (chunk > 2) ? B64_TABLE[triple & 0x3F] : '=';
i += chunk;
}
out[out_len] = '\0';
return out;
}
有的人就要问了,老师老师,怎么运行了没用呢,先把这个代码保存为base64.c,然后用下面的代码调用
#include <stdio.h>
#include <string.h>
#include <stdint.h>
char* base64_encode(const uint8_t* data, size_t len);
int main(void)
{
const char* txt = "hello, world";
char* encoded = base64_encode((const uint8_t*)txt, strlen(txt));
if (!encoded) {
fprintf(stderr, "malloc 失败\n");
return 1;
}
printf("原文: %s\n", txt);
printf("Base64: %s\n", encoded);
free(encoded);
return 0;
}
说在最后
这是第一次介绍一下自己,本人只是个小学语文老师,大学的时候迷恋上写代码,陆陆续续学了点,学艺不精啊,写一些笔记鼓励自己能一直学(虽然不知道学了干嘛。。。。)