项目简介
Base64转码工具,无论大大小小的项目,偶尔还是能用到的一个小工具,相当实用,这里通过C语言来做一个实现
项目背景
- 这个工具常用于一些 数据传输 or 数据保存 的场景
- 无论大大小小的项目,都是有数据存在的,通常都会进行 网络 or 本地 保存,正常情况下对于普通数据类型保存是没有问题的,但有时候作用于二进制数据显得不是那么方便,所以需要通过Base64转码处理,再通过Base64解码后再使用数据
实践过程
- 代码可于Github获取
- 首先确认两个功能,编码,解码
- 创建头文件 base64.h
#ifndef BASE64_BASE64_H
#define BASE64_BASE64_H
//base64编码
//b64:用于保存编码后的数据
//src:原数据
//size:原数据长度
//返回值:编码后的数据长度
int base64_encode(char **b64, char *src, int size);
//base64解码
//src:用于保存解码后的原始数据
//b64:编码数据
//size:编码数据的长度
//返回值:解码后的原数据长度
int base64_decode(char **src, char *b64, int size);
#endif //BASE64_BASE64_H
- 创建源文件 base64.c
base64 单元 4个 6位的整形 取值范围 0 - 63
源数据 8位 整形数组
每 3个 源数据元素 对应 1个 base64单元(PS:即24位为一组)
通过 共用体 + 位域 来实现 base64单元 与 源数据 的转换
#include "base64.h"
#include <stdlib.h>
//单个原数据长度
#ifdef BASE64_SRC_LEN
#undef BASE64_SRC_LEN
#endif
#define BASE64_SRC_LEN 3
//单个base64单元长度
#ifdef BASE64_UNIT_LEN
#undef BASE64_UNIT_LEN
#endif
#define BASE64_UNIT_LEN 4
//base64转码字符集
const char base64_charset[64] = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
'w', 'x', 'y', 'z', '0', '1', '2', '3',
'4', '5', '6', '7', '8', '9', '+', '/',
};
//对应关系: b3 b2 b1 b0
// c2 c1 c0
//base64单元
typedef union {
struct {
unsigned int b3 : 6;
unsigned int b2 : 6;
unsigned int b1 : 6;
unsigned int b0 : 6;
unsigned int : 8;
} unit;
struct {
unsigned int c2 : 8;
unsigned int c1 : 8;
unsigned int c0 : 8;
unsigned int : 8;
} src;
} base64_unit;
//初始化base64_unit
void base64_init_unit(base64_unit *unit, char *src);
//初始化base64_src
void base64_init_src(base64_unit *unit, char *src);
char base64_val2key(char val);
int base64_encode(char **b64, char *src, int size) {
//补位
const int fill = (BASE64_SRC_LEN - (size % BASE64_SRC_LEN)) % BASE64_SRC_LEN;
//base64字符串内存分配
const int b64_len = (size + fill) / BASE64_SRC_LEN * BASE64_UNIT_LEN;
*b64 = calloc(b64_len + 1, sizeof(char));
(*b64)[b64_len] = '\0';
//循环处理次数
const int count = (size + fill) / BASE64_SRC_LEN;
for (int i = 0; i < count; ++i) {
//根据原数据,构建单个b64_src数据
base64_unit unit;
base64_init_src(&unit, src + i * BASE64_SRC_LEN);
//base64转换并赋值
const int index = i * BASE64_UNIT_LEN;
(*b64)[index] = base64_charset[unit.unit.b0];
(*b64)[index + 1] = base64_charset[unit.unit.b1];
(*b64)[index + 2] = base64_charset[unit.unit.b2];
(*b64)[index + 3] = base64_charset[unit.unit.b3];
}
//补位赋值=
if (fill > 0) {
(*b64)[b64_len - 1] = '=';
}
if (fill > 1) {
(*b64)[b64_len - 2] = '=';
}
return b64_len;
}
int base64_decode(char **src, char *b64, int size) {
//补位
int fill = 0;
if (b64[size - 1] == '=') {
fill++;
}
if (b64[size - 2] == '=') {
fill++;
}
//原数据长度
const int src_len = size / BASE64_UNIT_LEN * BASE64_SRC_LEN;
*src = calloc(src_len + 1, sizeof(char));
//循环处理次数
const int count = size / BASE64_UNIT_LEN;
for (int i = 0; i < count; ++i) {
//根据base64单元数据
base64_unit unit;
base64_init_unit(&unit, b64 + i * BASE64_UNIT_LEN);
//base64转换并赋值
const int index = i * BASE64_SRC_LEN;
(*src)[index] = unit.src.c0;
(*src)[index + 1] = unit.src.c1;
(*src)[index + 2] = unit.src.c2;
}
(*src)[src_len - fill] = '\0';
return src_len - fill;
}
void base64_init_src(base64_unit *unit, char *src) {
unit->src.c0 = src[0];
unit->src.c1 = src[1];
unit->src.c2 = src[2];
}
void base64_init_unit(base64_unit *unit, char *src) {
unit->unit.b0 = base64_val2key(src[0]);
unit->unit.b1 = base64_val2key(src[1]);
unit->unit.b2 = base64_val2key(src[2]);
unit->unit.b3 = base64_val2key(src[3]);
}
char base64_val2key(char val) {
if (val == '/') {
return 63;
}
if (val == '+') {
return 62;
}
if (val >= 'a') {
return val - 'a' + 26;
}
if (val >= 'A') {
return val - 'A';
}
return val - '0' + 52;
}
- 使用栗子
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <base64.h>
int main() {
char *str = "https://juejin.cn/";
char *b64;
//b64编码
int b64_size = base64_encode(&b64, str, strlen(str));
printf("%s\nsize: %d\n", b64, b64_size);
char *dest;
//b64解码
int dest_size = base64_decode(&dest, b64, strlen(b64));
printf("%s\nsize: %d\n", dest, dest_size);
free(b64);
free(dest);
return 0;
}
//执行结果
//
// aHR0cHM6Ly9qdWVqaW4uY24v
// size: 24
// https://juejin.cn/
// size: 18
总结思考
为了方便日常使用,和提升自己的编码能力,手动实现一些常用的小工具
本文正在参与「掘金 2021 春招闯关活动」, 点击查看 活动详情