造轮子-Base64|项目复盘

721 阅读3分钟

项目简介

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 春招闯关活动」, 点击查看 活动详情