base64详解
base64详解
前置知识
位与字节
二进制系统中,每个0或1就是一个位(bit,比特),也叫存储单元,位是数据存储的最小单位。
其中8bit就称为一个字节(Byte)。
1B=8位
位运算
与运算:符号表示为&。运算规则:两位同时为“1”,结果才为“1”,否则为0或运算:符号表示为|。运算规则:两位只要有一位为“1”,结果就为“1”,否则为0
典型:
0000 0101 : 代表高四位, 代表低四位
char1&0x3:0x3是十六进制数,用二进制表示是0000 0011,用于提取char1的低两位,就是有1的那两位
char2&0xf0:0xf0是一个十六进制数,表示为二进制 11110000,用于提取 char2 的高四位。
移位操作
>>右移,<<左移
// 左移
01101000 << 2 -> 101000(左侧移出位被丢弃) -> 10100000(右侧空位一律补0)
// 右移
01101000 >> 2 -> 011010(右侧移出位被丢弃) -> 00011010(左侧空位一律补0)
base64编码
base64编码的概念
base64编码就是将字符串以每3个比特(bit)的字节子序列拆分为4个6比特(bit)的字节子序列(这个6比特是有效字节,最左边两个永远为0,其实也算是8比特的字节),再将得到的子序列查找base64的编码索引表,得到对应的字符拼接成新的字符串的一种编码方式。
每3个8比特(bit)的字节子序列拆分成4个6比特(bit)的字节序列的拆分过程如下图所示:
为什么base64编码后的大小是原来的4/3倍
因为6和8的最小公倍数是24,所以3个8比特的字节刚好可以拆分成4个6比特的字节,3 * 8 = 6 * 4。计算机中,因为一个字节需要8个存储单元存储,所以我们要把6个比特往前面补两位0,补足8个比特。如下图所示:
很明显,补足后所需的存储单元为32个,是原来所需的24个的4/3倍。现在大家明白为什么base64编码后的大小是原来的4/3倍了吧。
为什么命名为base64呢
因为6位的二进制有2的6次方个,也就是二进制数(0000 0000 - 0011 1111)之间的代表0-63的64个二进制数。
不是说一个字节是用8位二进制表示的吗,为什么不是2的8次方?
因为我们得到的8位二进制数的前两位永远是0,真正的有效位只有6位,所以我们所能够得到的二进制数只有2的6次方个。
base64字符是哪64个
Base64的编码索引表,字符选用了"A-Z、a-z、0-9、+、/" 64个可打印字符来代表(00000000-00111111)这64个二进制数。即
let base64EncodeChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
编码原理
我们不妨自己先思考一下,要把3个字节拆分成4个字节可以怎么做?你的实现思路和我的实现思路有哪个不同,我们之间又会碰出怎样的火花?
流程图
思路
分析映射关系:a b c -> x y z i
从高位到地位添加索引分析这个过程:
- x:(前面补2个0)a的前6位 => 00a[7]a[6] a[5]a[4]a[3]a[2]
- y:(前面补2个0)a的后2位 + b的前4位 => 00a[1]a[0] b[7]b[6]b[5]b[4]
- z:(前面补2个0)b的后4位 + c的前2位 => 00b[3]b[2] b[1]b[0]c[7]c[6]
- i:(前面补2个0)c的后6位 => 00c[5]c[4] c[3]c[2]c[1]c[0]
通过上面的分析,很容易得到实现思路:
- 将字符对应的ASCII编码转为8位的二进制
- 将每三个8位二进制数进行以下操作:
(1)将第一个数右移2位,得到第一个6位有效位二进制数
(2)将第一个数&0x3之后左移4位,得到第二个6位有效位二进制数的第一个和第二个有效位,将第二个数 & 0xf0之后右移位4位,得到第二个6位有效位二进制数的后四位有效位,两者取且得到第二个6位有 效位二进制
(3)将第二个数 & 0xf之后左移位2位,得到第三个6位有效位二进制数的前四位有效位,将第三个数 & 0xC0之后右移位6位,得到第三个6位有效位二进制数的后两位有效位,两者取且得到第三个6位有效 位二进制
(4)将第三个数 & 0x3f,得到第四个6位有效位二进制数
- 将获得的6位有效位二进制数转十进制,查找对应base64字符
注释:
char1&0x3的计算方法:先将char1和0x3转换为二进制,即0000 0011,按位与,作用是取char1的低两位
& 0xf0,即1111 0000,取高四位
& 0xf ,即0000 1111,取低四位
& 0xC0,即1100 0000,取高两位
& 0x3f ,即0011 1111,取低六位
base64编码实例
0x01
我们以hao字符串为例,观察base64编码的过程,我们将上面转换通过代码逻辑分析实现吧。
编码结果为aGFv
#include <stdio.h> char base64EncodeChars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; int main() {
char str[]="hao";
char char1,char2,char3,out1,out2,out3,out4;
char out[5]; // 将字符对应的ASCII值转为8位二进制数,& 0xff是为了保留8位二进制
char1 = str[0] & 0xff; // h 的ASCII为 104 二进制为 01101000
char2 = str[1] & 0xff; // a 的ASCII为 97 二进制为 01100001
char3 = str[2] & 0xff; // o 的ASCII为 111 二进制为 01101111 // 输出6位有效字节二进制数
out1 = char1 >> 2; // 26 011010
out2 = (char1 & 0x3) << 4 | (char2 & 0xf0) >> 4; // 6 000110
out3 = (char2 & 0xf) << 2 | (char3 & 0xc0) >> 6; // 5 000101
out4 = char3 & 0x3f; // 47 101111 out[0] = base64EncodeChars[out1]; //输出out1对应的ASCII
out[1] = base64EncodeChars[out2]; //输出out2对应的ASCII
out[2] = base64EncodeChars[out3]; //输出out3对应的ASCII
out[3] = base64EncodeChars[out4]; //输出out4对应的ASCII
out[4] = '\0'; //去掉输出末尾的'hao' printf("%s",out); return 0;
}
0x02
扩展至多字符字符串
#include<stdio.h>
#include<string.h> char base64EncodeChars[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" ; int main(){
char str[]="haohaohao";
char char1,char2,char3,out1,out2,out3,out4;
char out[100];
int len = strlen(str);
int index = 0;
int outindex = 0; while(index < len){
char1 = str[index++] & 0xff ;
char2 = str[index++] & 0xff ;
char3 = str[index++] & 0xff ; out1 = char1 >> 2 ;
out2 = (char1 & 0x3) << 4 | (char2 & 0xf0) >> 4 ;
out3 = (char2 & 0xf) << 2 | (char3 & 0xc0) >> 6 ;
out4 = char3 & 0x3f ; out[outindex++] = base64EncodeChars[out1];
out[outindex++] = base64EncodeChars[out2];
out[outindex++] = base64EncodeChars[out3];
out[outindex++] = base64EncodeChars[out4]; }
out[outindex] = '\0';
printf("%s\n",out);
return 0;
}
0x03
究极版base64编码
#include <stdio.h>
#include <stdlib.h> char* base64Encode(const char* str) {
// Base64 characters
const char base64EncodeChars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; // Get string length
int len = 0;
while (str[len] != '\0') {
len++;
} // Output string
char* out = (char*)malloc(len * 4 / 3 + 4);
char* v11 = out; int index = 0; while (index < len) {
// Define input and output bytes
unsigned char char1, char2, char3;
unsigned char out1, out2, out3, out4; // Convert characters to ASCII codes
char1 = str[index++] & 0xff;
out1 = char1 >> 2; if (index == len) {
out2 = (char1 & 0x3) << 4;
sprintf(v11, "%c%c==", base64EncodeChars[out1], base64EncodeChars[out2]);
break;
} char2 = str[index++] & 0xff;
out2 = ((char1 & 0x3) << 4) | ((char2 & 0xf0) >> 4); if (index == len) {
out3 = (char2 & 0xf) << 2;
sprintf(v11, "%c%c%c=", base64EncodeChars[out1], base64EncodeChars[out2], base64EncodeChars[out3]);
break;
} char3 = str[index++] & 0xff;
out3 = ((char2 & 0xf) << 2) | ((char3 & 0xc0) >> 6);
out4 = char3 & 0x3f; sprintf(v11, "%c%c%c%c", base64EncodeChars[out1], base64EncodeChars[out2], base64EncodeChars[out3], base64EncodeChars[out4]);
v11 += 4;
} return out;
} int main() {
char* encodedStr = base64Encode("haohao");
printf("%s\n", encodedStr);
free(encodedStr); encodedStr = base64Encode("haoha");
printf("%s\n", encodedStr);
free(encodedStr); encodedStr = base64Encode("haoh");
printf("%s\n", encodedStr);
free(encodedStr); return 0;
}
base64自定义字符编码
#include <stdio.h>
#include <stdlib.h> char* base64Encode(const char* str) {
// Base64 characters
const char base64EncodeChars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; // Get string length
int len = 0;
while (str[len] != '\0') {
len++;
} // Output string
char* out = (char*)malloc(len * 4 / 3 + 4);
char* v11 = out; int index = 0; while (index < len) {
// Define input and output bytes
unsigned char char1, char2, char3;
unsigned char out1, out2, out3, out4; // Convert characters to ASCII codes
char1 = str[index++] & 0xff;
out1 = char1 >> 2; if (index == len) {
out2 = (char1 & 0x3) << 4;
sprintf(v11, "%c%c==", base64EncodeChars[out1], base64EncodeChars[out2]);
break;
} char2 = str[index++] & 0xff;
out2 = ((char1 & 0x3) << 4) | ((char2 & 0xf0) >> 4); if (index == len) {
out3 = (char2 & 0xf) << 2;
sprintf(v11, "%c%c%c=", base64EncodeChars[out1], base64EncodeChars[out2], base64EncodeChars[out3]);
break;
} char3 = str[index++] & 0xff;
out3 = ((char2 & 0xf) << 2) | ((char3 & 0xc0) >> 6);
out4 = char3 & 0x3f; sprintf(v11, "%c%c%c%c", base64EncodeChars[out1], base64EncodeChars[out2], base64EncodeChars[out3], base64EncodeChars[out4]);
v11 += 4;
} return out;
} int main() {
char YourStr[100];
scanf("%s",&YourStr); char* encodedStr = base64Encode(YourStr);
printf("%s\n", encodedStr);
free(encodedStr); return 0;
}
解码原理
逆向推导,由每4个6位有效位的二进制数合并为3个8位二进制数,根据ASCII编码映射到对应字符后拼接字符串。
思路
分析映射关系x y z i -> a b c
- a:x的后6位 + y的第5、6位 => x[5] x[4] x[3] x[2] x[1] x[0] y[5] y[4]
- b:y的后4位 + z的第3、4、5、6位 => y[3] y[2] y[1] y[0] z[5] z[4] z[3] z[2]
- c:z的后2位 + i的后6位 => z[1] z[0] i[5] i[4] i[3] i[2] i[1] i[0]
- 将字符对应的base64字符集的索引转为6位有效位二进制数
- 将每四个6位有效位二进制数进行以下操作
(1)第一个二进制数左移位2位,得到新二进制数的前6位,第二个二进制数 & 0x30之后右移位4位, 或运算后得到第一个新二进制数
(2)第二个二进制数 & 0xf之后左移位4位,第三个二进制数 & 0x3c之后右移位2位,或运算后得到 第二个新二进制数
(3)第二个二进制数 & 0x3之后左移位6位,与第四个二进制数或运算后得到第二个新二进制数
- 根据ascII编码映射到对应字符后拼接字符串
base64解码实例
只有三个字符版本
#include <stdio.h>
#include <string.h> int main() {
char str[] = "aGFv";
char base64CharsArr[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
int char1, char2, char3, char4;
int out1, out2, out3;
char out[4]; // 获取索引值
char *ptr = strchr(base64CharsArr, str[0]);
if (ptr != NULL) {
char1 = ptr - base64CharsArr;
}
ptr = strchr(base64CharsArr, str[1]);
if (ptr != NULL) {
char2 = ptr - base64CharsArr;
}
ptr = strchr(base64CharsArr, str[2]);
if (ptr != NULL) {
char3 = ptr - base64CharsArr;
}
ptr = strchr(base64CharsArr, str[3]);
if (ptr != NULL) {
char4 = ptr - base64CharsArr;
} // 位运算
out1 = (char1 << 2) | ((char2 & 0x30) >> 4);
out2 = ((char2 & 0xf) << 4) | ((char3 & 0x3c) >> 2);
out3 = ((char3 & 0x3) << 6) | char4; // 输出结果
out[0] = out1;
out[1] = out2;
out[2] = out3;
out[3] = '\0';
printf("%s\n", out); return 0;
}
遇到有用'='补过位的情况时
#include <stdio.h>
#include <string.h>
#include <stdlib.h> char base64CharsArr[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; void base64decode(char str[]) {
int length = strlen(str);
int padding = 0; // 计算填充字符数量
if (str[length - 1] == '=') {
padding++;
if (str[length - 2] == '=')
padding++;
} // 计算解码后的字符数量
int decodedLength = (length * 3) / 4 - padding; // 分配存储解码结果的内存
char* decodedStr = (char*)malloc(decodedLength + 1); int outIndex = 0;
for (int i = 0; i < length; i += 4) {
char char1 = -1, char2 = -1, char3 = -1, char4 = -1; // 查找每个字符在Base64字符集中的索引
for (int j = 0; j < 64; j++) {
if (base64CharsArr[j] == str[i]) {
char1 = j;
break;
}
} for (int j = 0; j < 64; j++) {
if (base64CharsArr[j] == str[i + 1]) {
char2 = j;
break;
}
} for (int j = 0; j < 64; j++) {
if (base64CharsArr[j] == str[i + 2]) {
char3 = j;
break;
}
} for (int j = 0; j < 64; j++) {
if (base64CharsArr[j] == str[i + 3]) {
char4 = j;
break;
}
} // 解码并存储结果
decodedStr[outIndex++] = (char1 << 2) | ((char2 & 0x30) >> 4);
if (char3 != -1)
decodedStr[outIndex++] = ((char2 & 0xf) << 4) | ((char3 & 0x3c) >> 2);
if (char4 != -1)
decodedStr[outIndex++] = ((char3 & 0x3) << 6) | char4;
} // 添加字符串结束符
decodedStr[decodedLength] = '\0'; printf("Decoded string: %s\n", decodedStr); // 释放内存
free(decodedStr);
} int main() {
char str[] = "SGVsbG8gd29ybGQ="; // Example base64 encoded string
base64decode(str);
return 0;
}
解码整个字符串,整理代码后
#include <stdio.h>
#include <string.h>
#include <stdlib.h> char* base64decode(const char* str) {
// Base64字符集
const char base64CharsArr[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
int i = 0;
int len = strlen(str);
char* out = (char*)malloc(len * 3 / 4 + 1); // 分配足够的内存来存储解码后的字符串
int outIndex = 0; while (i < len) {
int char1 = strchr(base64CharsArr, str[i]) - base64CharsArr;
i++;
int char2 = strchr(base64CharsArr, str[i]) - base64CharsArr;
i++;
int out1, out2, out3; if (char1 == -1 || char2 == -1) {
out[outIndex] = '\0';
return out;
} char1 = char1 & 0xff;
char2 = char2 & 0xff; int char3 = strchr(base64CharsArr, str[i]) - base64CharsArr;
i++; // 第三位不在base64对照表中时,只拼接第一个字符串
out1 = (char1 << 2) | ((char2 & 0x30) >> 4);
if (char3 == -1) {
out[outIndex++] = out1;
out[outIndex] = '\0';
return out;
} int char4 = strchr(base64CharsArr, str[i]) - base64CharsArr;
i++; // 第四位不在base64对照表中时,只拼接第一个和第二个字符串
out2 = ((char2 & 0xf) << 4) | ((char3 & 0x3c) >> 2);
if (char4 == -1) {
out[outIndex++] = out1;
out[outIndex++] = out2;
out[outIndex] = '\0';
return out;
} // 位运算
out3 = ((char3 & 0x3) << 6) | char4; out[outIndex++] = out1;
out[outIndex++] = out2;
out[outIndex++] = out3;
} out[outIndex] = '\0';
return out;
} int main() {
char* decodedStr = base64decode("aGFvaGFv");
printf("%s\n", decodedStr); // 输出: haohao
free(decodedStr); decodedStr = base64decode("aGFvaGE=");
printf("%s\n", decodedStr); // 输出: haoha
free(decodedStr); decodedStr = base64decode("aGFvaA==");
printf("%s\n", decodedStr); // 输出: haoh
free(decodedStr); return 0;
}
总结
说起Base64编码可能有些奇怪,因为大多数的编码都是由字符转化成二进制的过程,而从二进制转成字符的过程称为解码。而Base64的概念就恰好反了,由二进制转到字符称为编码,由字符到二进制称为解码。Base64 是一种数据编码方式,可做简单加密使用,我们可以改变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 |
/ |
|||
参考:https://zhuanlan.zhihu.com/p/408318391
base64详解的更多相关文章
- ArrayBuffer转base64详解
先贴代码: const base64String = window.btoa(String.fromCharCode(... new Uint8Array(buffer))) 看起来非常的简洁,优美. ...
- Java 8新特性探究(五)Base64详解
BASE64 编码是一种常用的字符编码,在很多地方都会用到.但base64不是安全领域下的加密解密算法.能起到安全作用的效果很差,而且很容易破解,他核心作用应该是传输数据的正确性,有些网关或系统只能使 ...
- base64详解及实现
概述 base64 说起来大家应该都是很熟悉的,很多类型的数据都可以转成base64的编码规则,例如图片,pdf,文本,邮件内容等. 什么是base64 根据RFC2045的定义,base64被定义为 ...
- Android 数据加密算法 Des,Base64详解
一,DES加密: 首先网上搜索了一个DES加密算法工具类: import java.security.*;import javax.crypto.*; public class DesHelper { ...
- Java 8新特性探究(十一)Base64详解
开发十年,就只剩下这套架构体系了! >>> BASE64 编码是一种常用的字符编码,在很多地方都会用到.但base64不是安全领域下的加密解密算法.能起到安全作用的效果很差,而且 ...
- 图片上传预览转压缩并转base64详解(dShowImg64.js)
hello,大家好,游戏开始了,欢迎大家收看这一期的讲解.本次的内容是图片的上传预览.最后发源码链接.废话不多说,先上图. 待上传图像 点击蓝色框内,pc可以选择文件,移动端选择拍照或选择图片进行上传 ...
- Base64编码格式详解
什么是Base64? 按照RFC2045的定义,Base64被定义为:Base64内容传送编码被设计用来把任意序列的8位字节描述为一种不易被人直接识别的形式.(The Base64 Content-T ...
- EF+LINQ事物处理 C# 使用NLog记录日志入门操作 ASP.NET MVC多语言 仿微软网站效果(转) 详解C#特性和反射(一) c# API接受图片文件以Base64格式上传图片 .NET读取json数据并绑定到对象
EF+LINQ事物处理 在使用EF的情况下,怎么进行事务的处理,来减少数据操作时的失误,比如重复插入数据等等这些问题,这都是经常会遇到的一些问题 但是如果是我有多个站点,然后存在同类型的角色去操作 ...
- javascript中的Base64.UTF8编码与解码详解
javascript中的Base64.UTF8编码与解码详解 本文给大家介绍的是javascript中的Base64.UTF8编码与解码的函数源码分享以及使用范例,十分实用,推荐给小伙伴们,希望大家能 ...
- 详解前端模块化工具-webpack
webpack是一个module bundler,抛开博大精深的汉字问题,我们暂且管他叫'模块管理工具'.随着js能做的事情越来越多,浏览器.服务器,js似乎无处不在,这时,使日渐增多的js代码变得合 ...
随机推荐
- 【SSM项目】尚筹网(二)基于Servlet3.0项目搭建:日志系统以及声明式事务
1 日志系统 常见的日志系统实现log4j.JUL(jdk自带).log4j2.logback(和SLF4J同一个作者,能够天然衔接),这些实现就类似于java的接口实现,而SLF4J就类似于java ...
- Ubuntu Server搭建个人服务器
Ubuntu Server20.04.5 LTS [参考资料] Ubuntu官方地址:https://www.ubuntu.com/ Ubuntu论坛地址:https://ubuntuforums.o ...
- dotnet初探:用miniapi创建一个自己的url
致谢 首先写在前面,非常感谢微软mvp桂素伟先生的技术分享,因为微软的文档大部分都如机器翻译般的生硬,让人难以读下去,正是他的无私分享为我的.net学习旅程提供了方向,非常感谢.如果大家对他比较感兴趣 ...
- Python全栈开发工程师 day57 jQuery
二.jQuery样式操作标签样式操作<!DOCTYPE html><html lang="en"><head> <meta charset ...
- Git代码提交规范
1. 引言 思想,因人而异,难以重复 写代码时,每个人的习惯是不一样的,所以,引入了代码规范,为了省力,引入了自动格式化代码工具,前端工程中比较典型的自动格式化代码工具如:Prettier · Opi ...
- 一文讲透产品经理如何用好ChatGPT
作者:京东零售 何雨航 "4.0版本的ChatGPT可以有效提升产品经理工作效率,但并无法替代产品经理的角色." 一.引言 3月15日,OpenAI发布了最新的基于GPT-4的Ch ...
- Java SE 20 新增特性
Java SE 20 新增特性 作者:Grey 原文地址: 博客园:Java SE 20 新增特性 CSDN:Java SE 20 新增特性 源码 源仓库: Github:java_new_featu ...
- 2020-10-25:go中channel的close流程是什么?
福哥答案2020-10-25:
- 使用 MRKL 系统跨越神经符号鸿沟
本文展示了自然语言处理的下一步发展--模块化推理.知识和语言( the Modular Reasoning, Knowledge and Language,简称为MRKL)系统以及LangChain和 ...
- Vue全局公共服务类mixin
首先,简单介绍下mixin: Mixin是面向对象程序设计语言中的类,提供了方法的实现.其他类可以访问mixin类的方法而不必成为其子类 Mixin类通常作为功能模块使用,在需要该功能时"混 ...