【C语言】C语言编程实战:Base64编解码算法从理论到实现(附完整代码)
I'mAlex 2024-06-12 15:05:03 阅读 72
C语言编程实战:Base64编解码算法从理论到实现(附完整代码)
🧑 作者简介:现任阿里巴巴嵌入式技术专家,15年工作经验,深耕嵌入式+人工智能领域,精通嵌入式领域开发、技术管理、简历招聘面试。CSDN优质创作者,提供产品测评、学习辅导、简历面试辅导、毕设辅导、项目开发、C/C++/Java/Python/Linux/AI等方面的服务,如有需要请站内私信或者联系任意文章底部的的VX名片(ID:gylzbk)
💬 博主粉丝群介绍:① 群内高中生、本科生、研究生、博士生遍布,可互相学习,交流困惑。② 热榜top10的常客也在群里,也有数不清的万粉大佬,可以交流写作技巧,上榜经验,涨粉秘籍。③ 群内也有职场精英,大厂大佬,可交流技术、面试、找工作的经验。④ 进群免费赠送写作秘籍一份,助你由写作小白晋升为创作大佬。⑤ 进群赠送CSDN评论防封脚本,送真活跃粉丝,助你提升文章热度。有兴趣的加文末联系方式,备注自己的CSDN昵称,拉你进群,互相学习共同进步。
🗄️ 专栏介绍:本文归属于专栏《C/C++》,专栏文章平均质量分92,持续更新中,欢迎大家免费订阅关注。
C语言编程实战:Base64编解码算法从理论到实现(附完整代码)
1. 概述2. 原理2.1 Base64编码表2.2 Base64编码步骤2.3 Base64解码步骤 3. 核心代码解读4. 完整代码下载5. 总结
1. 概述
Base64算法是一种基于64个字符的编码算法,常用于在通常处理文本数据的场合,表示、传输、存储一些二进制数据。该算法使用可打印字符集来表示二进制数据,使得数据可以在文本格式中安全地传输和存储。
2. 原理
为了保证所输出的编码为可读字符,Base64制定了一个由特定ASCII码组成的编码表,以便进行统一编码转换。编码表的大小为2^6=64
,这就是Base64名称的由来。
如下所示,Base64编码表包括A-Z
、a-z
、0-9
、+/
共64个可打印字符。
2.1 Base64编码表
码值 | 字符 | | | 码值 | 字符 | | | 码值 | 字符 | | | 码值 | 字符 |
---|---|---|---|---|---|---|---|---|---|---|
0 | A | | | 16 | Q | | | 32 | g | | | 48 | w |
1 | B | | | 17 | R | | | 33 | h | | | 49 | x |
2 | C | | | 18 | S | | | 34 | i | | | 50 | y |
3 | D | | | 19 | T | | | 35 | j | | | 51 | z |
4 | E | | | 20 | U | | | 36 | k | | | 52 | 0 |
5 | F | | | 21 | V | | | 37 | l | | | 53 | 1 |
6 | G | | | 22 | W | | | 38 | m | | | 54 | 2 |
7 | H | | | 23 | X | | | 39 | n | | | 55 | 3 |
8 | I | | | 24 | Y | | | 40 | o | | | 56 | 4 |
9 | J | | | 25 | Z | | | 41 | p | | | 57 | 5 |
10 | K | | | 26 | a | | | 42 | q | | | 58 | 6 |
11 | L | | | 27 | b | | | 43 | r | | | 59 | 7 |
12 | M | | | 28 | c | | | 44 | s | | | 60 | 8 |
13 | N | | | 29 | d | | | 45 | t | | | 61 | 9 |
14 | O | | | 30 | e | | | 46 | u | | | 62 | + |
15 | P | | | 31 | f | | | 47 | v | | | 63 | / |
2.2 Base64编码步骤
将源数据划分为每3
个字节一组,一组组的进行运算。将每一组的3
个字节转换为二进制,得到24
位的数据(如果最后一组源数据不足3
字节,不足的部分补0
,如M
补成M00
,Ma
补成Ma0
)。将24
位数据按照每6
位一组进行重新划分,得到4
组长度为6
位的新数据。将每组6
位长度的新数据(高2位补0)转换为10进制数,得到一个范围为[0, 63)
之间的数字。4组新数据共得到4
个10进制数字。拿上一步计算出的4
个10进制数字分别去查Base64编码表,得到4
个ASCII符号,将其连起来,得到一个4
字节长度的字符串,这就是这一组3
字节源数据计算得到的base64值了。重复步骤2-5
,直到计算完成。把每一组3字节源数据计算得到的base64值连接起来。如果源数据长度刚好是3的整数倍,那么上一步计算完就得到最终的base64编码了。如果源数据最后一组不足3字节,可能只有1
个或2
个字节,这时存在2
个或者1
个填充标记(0
),此时需要将上一步计算出的base64编码结尾1到2个字符替换为=
,有几个填充标记就就替换为几个=
。以标示实际数据只占原来编码的一部分,解码时需要把这部分数据排除掉。 理论总是抽象的,下面我们直接看例子。
举例子
假设需要编码的文本字符串是Man
(在ASCII中,M=77, a=97, n=110)。
将字符转换为ASCII值: M a n 77 97 110 将ASCII值转换为二进制: 01001101 01100001 01101110 重新分组为4个6位的单元: 010011 010110 000101 101110 将这些6位的单元转换为十进制: 19 22 5 46 根据Base64索引表找到对应的字符: T W F u
因此,Man
这个字符串的Base64编码结果是TWFu
。
假设我们有一个字符串Ma
,它只有两个字节(在ASCII中,M=77, a=97)。
首先将字符转换为ASCII值,再将ASCII值转换为二进制形式: M a 77 97 01001101 01100001 现在我们只有两个字节,少于一个完整的3字节组。 为了形成一个完整的3字节组,我们需要对最后一个不完整的字节组进行填充。 在这个例子中,我们添加一个字节的填充,即8个比特位的0。 01001101 01100001 00000000 接下来,我们将这个24位的数据重新划分为4个6位的单元: 010011 010110 000100 000000 将这些6位的单元转换为十进制数,然后对照Base64编码表找到相应的字符: 19 22 4 0 T W E A 然而,因为我们进行了填充,所以要在Base64编码后加上等号。 在这个例子中,我们进行了一个字节的填充,所以在Base64编码末尾添加一个等号。
因此,字符串Ma
的Base64编码结果是TWE=
。
假设我们有一个字符串Ma
,它只有一个字节(在ASCII中,M=77)。
首先将字符 "M"转换为ASCII值,并将该值转换为二进制形式: M 77 二进制: 01001101 现在我们只有一个字节,远少于一个完整的3字节组。为了形成一个完整的3字节组,我们需要对其进行填充。在这个例子中,我们添加两个字节的填充,即16个比特位的0: 01001101 00000000 00000000 接下来,我们将这个24位的数据重新划分为4个6位的单元: 010011 010000 000000 000000 将这些6位的单元转换为十进制数,然后对照Base64编码表找到相应的字符: 19 16 0 0 T Q A A 由于我们进行了填充,所以要在Base64编码后加上两个等号==,以标示实际数据只占原来编码的一部分。在这个例子中,我们进行了两个字节的填充,所以在Base64编码末尾添加两个等号。
因此,字符串M
的Base64编码结果是TQ==
。
2.3 Base64解码步骤
解码Base64编码的过程与编码相反,将每个Base64字符转换为对应的6
位二进制值,然后将这些6
位值组合成原始的二进制数据,再还原回去即可。
3. 核心代码解读
base64编码表char *base64_encodetable = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
base64最终结果长度计算
//每3个字节一组转化为4字节//因此计算可以分多少组,将组数乘以4就是编码后的长度codelength = ((length / 3) + (length % 3 > 0 ? 1 : 0)) * 4;
每3个字节一组转化为4字节并进行计算
/* * src为源数据 * length为源数据长度 * base64code用于存储最终的base64编码结果 **///每3个字节分一组,调用_base64_section_encode()计算其base64,然后把每一组的base64值拼接起来。for (i = 0; i < length / 3; i++) { tmp = _base64_section_encode(src + i * 3, 3);strcat(base64code, tmp);}//如果源数据长度不是3个整数倍,那么将剩余的1个或者2个字节数据单独分一组计算其base64,然后把计算出的base64值拼接到之前的结果。if (length % 3) { tmp = _base64_section_encode(src + length - (length % 3), length % 3);strcat(base64code, tmp);}
源数据3字节分组计算base64
//数字字符串表static char *num_table = "0123456789";/* * char类型数转换为2进制字符串格式, 如2->"10", 6->"110" **/static char *char2binstr(char value){ int i = 0;char binstr[9] = { };//取到每一位之后,查表得到对应的字符拼接成一个字符串for (i = 0; i < 8; i++)binstr[7 - i] = num_table[(value & (0x1 << i)) >> i];return strdup(binstr);}/* * 2进制字符串格式数据转换为10进制数字,如"10"->2, "110"->6 **/static char binstr2char(char *binstr){ int i = 0;char value = 0;int length = 0;if (!binstr)return 0;length = strlen(binstr);//取得2进制字符串的每一个字节,将其转化对应的数字,然后还原数据。for (i = 0; i < length; i++)value += (binstr[length - 1 - i] - 0x30) << i;return value;}/* * 将源数据每3字节作为一组数据进行一次转换,并计算这一组的base64值。 **/static char *_base64_section_encode(char *substr, int length){ int i = 0;/* * 用于存储每一组数据计算得到的base64值。 * 每个分组计算完base64,都会得到一个4字节长度的ASCII码。即使源数据不足3字节,也会在计算出的base64结果后面使用=填充到4个字节。 * 这里我们直接把4个字节全部预初始化为=,这样做的好处是当最后一次运算源数据不足3位时,就不用再补位了,节省几行代码。 **/char dest[5] = "====";/* * 用于存储转换后的24字节二进制数格式字符串。当源数据不足3字节时,低位补0填充。 * 这里我们直接把24字节全部预初始化为0,当源数据不足3位时,就不用再补位了,节省代码。 **/char binstr[24] = "000000000000000000000000";//存储运算过程中的一些临时结果char tmp[7] = { };char *tmp1 = NULL;//先将源数据转换为一个连续的2进制格式字符串,长度为24字节for (i = 0; i < length; i++) { tmp1 = char2binstr(substr[i]);strncpy(binstr + i * 8, tmp1, 8);free(tmp1);}/* * 转换后得到的24字节2进制格式字符串,分为4组,每组取6个字节,将这6个字节的2进制格式字符串转换回字符,将字符(大小范围是0-63)作为数组下标查表得到一个ASCII字符。 * 这里分3种情况: * 1.如果最后一次转换只有1个字节数据,那么会转换出2个新字节,即查表2次,此时还剩余2个空白字节不参与转换,需要在编码结果后加2个=补位。 * 2.如果最后一次转换只有2个字节数据,那么会转换出3个新字节,即查表3次,此时还剩余1个空白字节不参与转换,需要在编码结果后加1个=补位。 * 3.如果最后一次转换有完整的3个字节数据,那么会转换出完整的4个新字节,即查表4次,此时无需补位。 * 由此可见,实际计算次数此时就是在源数据字节数基础上+1。源数据字节数length的可能值是1、2、3 **/for (i = 0; i < length + 1; i++) { strncpy(tmp, binstr + 6 * i, 6);dest[i] = base64_encodetable[binstr2char(tmp)];}return strdup(dest);}
编码流程的核心代码如上所示,解码过程大同小异,这里就不再讲解了,请自行下载完整代码查阅。
为了提高效率,代码逻辑上做了一些优化,并非逐字逐句按照编码步骤编写的代码。当然,也还存在很多优化点,比如二进制转换部分其实可以不用使用字符串,使用位运算来替换,在高频应用场景下,可以进一步提高算法执行效率。
4. 完整代码下载
代码写于多年之前,已应用到很多项目中,欢迎大家拍砖。该项目起初开源于码云平台,现已mirror到CSDN的gitcode平台,代码地址:https://gitcode.com/g310773517/base64。欢迎大家Watch、Star、Fork。
5. 总结
Base64编码具有以下特点:
编码后的数据长度总是比原始数据长约 1/3。Base64 编码是一种可逆的编码方式,可以通过解码还原出原始数据。
总的来说,Base64算法是一种方便、简单且广泛使用的编码方式,用于在文本格式中安全地传输和存储二进制数据。
声明
本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。