0%

What is Base64 code

Base64 编码和解码

Base64是一种基于64个可打印字符来表示二进制数据的编码方式。它通常用于在文本数据中传输二进制数据,例如电子邮件和URL。

Base64编码过程

  1. 数据分割:将输入的二进制数据按每24位(3字节)一组分割。如果最后一组不足24位,用0进行填充。
  2. 每6位一组:将每24位的数据再分割成4个6位的数据块。
  3. 映射到字符:每个6位的数据块对应一个Base64字符。Base64字符表包含A-Z、a-z、0-9、+和/,共64个字符。
  4. 填充字符:如果输入数据长度不是3的倍数,输出会填充一个或两个’=’字符,使其长度为4的倍数。

Base64解码过程

  1. 去除填充:将输入的Base64字符串中的’=’去除。
  2. 映射到二进制:将每个Base64字符映射回6位二进制数据。
  3. 合并数据块:将6位的数据块按顺序合并成原始的8位数据块。
  4. 移除填充位:去除由于编码时填充的0位,恢复原始数据。

主要用途

  1. 数据传输:Base64编码可以将二进制数据转换为只包含ASCII字符的字符串,便于通过电子邮件、HTTP、FTP等传输协议发送。这些协议有时不支持原始二进制数据,或者会对某些字符进行转义处理,因此Base64编码可以确保数据在传输过程中不被损坏。
  2. 数据存储:在某些数据库或文本文件中,只能存储文本数据。Base64编码可以将二进制数据转换为文本格式,方便存储和读取。
  3. 数据嵌入:在Web开发中,可以将图片、音频等文件通过Base64编码直接嵌入到HTML、CSS或JavaScript文件中。例如,使用Base64编码的图片可以嵌入到CSS文件中作为背景图像,减少HTTP请求次数,提高网页加载速度。
  4. 安全性和隐私:虽然Base64编码并不是一种加密方法,但在某些情况下,可以用作简单的混淆手段,使得数据不易被直接读取。例如,在URL中使用Base64编码可以避免直接暴露敏感信息。

注意事项

  1. 效率:Base64编码后的数据比原始二进制数据大约大33%,因此对于大数据量的传输或存储,可能不是最佳选择。
  2. 安全性:Base64编码仅是一种编码方式,不提供任何安全保障。如果需要保护数据安全,应该使用加密算法。

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>

// Base64字符表
const char base64_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

// Base64字符反向映射表
static const int base64_chars_inverse[] = {
// ASCII值到Base64索引的映射
[43] = 62, [47] = 63, [48] = 52, [49] = 53, [50] = 54, [51] = 55,
[52] = 56, [53] = 57, [54] = 58, [55] = 59, [56] = 60, [57] = 61,
[65] = 0, [66] = 1, [67] = 2, [68] = 3, [69] = 4, [70] = 5,
[71] = 6, [72] = 7, [73] = 8, [74] = 9, [75] = 10, [76] = 11,
[77] = 12, [78] = 13, [79] = 14, [80] = 15, [81] = 16, [82] = 17,
[83] = 18, [84] = 19, [85] = 20, [86] = 21, [87] = 22, [88] = 23,
[89] = 24, [90] = 25, [97] = 26, [98] = 27, [99] = 28, [100] = 29,
[101] = 30, [102] = 31, [103] = 32, [104] = 33, [105] = 34, [106] = 35,
[107] = 36, [108] = 37, [109] = 38, [110] = 39, [111] = 40, [112] = 41,
[113] = 42, [114] = 43, [115] = 44, [116] = 45, [117] = 46, [118] = 47,
[119] = 48, [120] = 49, [121] = 50, [122] = 51
};

// Base64编码函数
char *base64_encode(const unsigned char *data, size_t input_length, size_t *output_length) {
// 计算输出长度,Base64编码后的长度是输入长度的4/3并向上取整
*output_length = 4 * ((input_length + 2) / 3);
char *encoded_data = malloc(*output_length + 1); // 分配内存
if (encoded_data == NULL) return NULL;

// 遍历输入数据,按每3字节一组进行处理
for (size_t i = 0, j = 0; i < input_length;) {
// 三个字节一组,输入长度非3的倍数,需要在组内末尾补0
uint32_t octet_a = i < input_length ? data[i++] : 0;
uint32_t octet_b = i < input_length ? data[i++] : 0;
uint32_t octet_c = i < input_length ? data[i++] : 0;

// 合并三个字节为24位
// octet_a << 0x10:将 octet_a 左移16位,即将 octet_a 放在24位数的高8位。
// octet_b << 0x08:将 octet_b 左移8位,即将 octet_b 放在24位数的中间8位。
// octet_c:保持 octet_c 在最低8位。
uint32_t triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c;

// 分别取出高6位、中6位和低6位,并根据Base64字符表进行转换
// triple 右移18位(即 3 * 6),取最低6位,在通过Base64字符表进行转换
encoded_data[j++] = base64_chars[(triple >> 3 * 6) & 0x3F];
encoded_data[j++] = base64_chars[(triple >> 2 * 6) & 0x3F];
encoded_data[j++] = base64_chars[(triple >> 1 * 6) & 0x3F];
encoded_data[j++] = base64_chars[(triple >> 0 * 6) & 0x3F];
}

// 添加填充字符
static const int mod_table[] = {0, 2, 1};
for (size_t i = 0; i < mod_table[input_length % 3]; ++i)
encoded_data[*output_length - 1 - i] = '=';

// 添加字符串结束符
encoded_data[*output_length] = '\0';
return encoded_data;
}

// Base64解码函数
unsigned char *base64_decode(const char *data, size_t input_length, size_t *output_length) {
if (input_length % 4 != 0) return NULL; // Base64输入长度必须是4的倍数

// 计算输出长度
*output_length = input_length / 4 * 3;
if (data[input_length - 1] == '=') (*output_length)--;
if (data[input_length - 2] == '=') (*output_length)--;

unsigned char *decoded_data = malloc(*output_length); // 分配内存
if (decoded_data == NULL) return NULL;

// 遍历输入数据,按每4字符一组进行处理
for (size_t i = 0, j = 0; i < input_length;) {
// 检查当前字符是否是 ‘=’
// 如果是 =,则结果为0(由于按位与运算与 0 的结果总是 0),并且 i 递增
// 如果不是 =,则查找 data[i] 在 base64_chars_inverse 表中的映射值,并且 i 递增。
uint32_t sextet_a = data[i] == '=' ? 0 & i++ : base64_chars_inverse[data[i++]];
uint32_t sextet_b = data[i] == '=' ? 0 & i++ : base64_chars_inverse[data[i++]];
uint32_t sextet_c = data[i] == '=' ? 0 & i++ : base64_chars_inverse[data[i++]];
uint32_t sextet_d = data[i] == '=' ? 0 & i++ : base64_chars_inverse[data[i++]];

// 合并4个6位数为24位
uint32_t triple = (sextet_a << 3 * 6) + (sextet_b << 2 * 6) + (sextet_c << 1 * 6) + (sextet_d << 0 * 6);

// 分别取出高8位、中8位和低8位
// 将 triple 右移16位(即 2 * 6),取最低8位,在通过base64_chars_inverse字符表进行转换
if (j < *output_length) decoded_data[j++] = (triple >> 2 * 8) & 0xFF;
if (j < *output_length) decoded_data[j++] = (triple >> 1 * 8) & 0xFF;
if (j < *output_length) decoded_data[j++] = (triple >> 0 * 8) & 0xFF;
}

return decoded_data;
}

示例程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

int main() {
// 示例数据
const char *data = "Hello, world!";
size_t data_length = strlen(data);

// Base64编码
size_t encoded_length;
char *encoded_data = base64_encode((const unsigned char *)data, data_length, &encoded_length);
if (encoded_data == NULL) {
fprintf(stderr, "Failed to encode data to Base64\n");
return 1;
}
printf("Base64 Encoded: %s\n", encoded_data);

// Base64解码
size_t decoded_length;
unsigned char *decoded_data = base64_decode(encoded_data, encoded_length, &decoded_length);
if (decoded_data == NULL) {
fprintf(stderr, "Failed to decode Base64 data\n");
free(encoded_data);
return 1;
}
printf("Base64 Decoded: %.*s\n", (int)decoded_length, decoded_data);

// 清理内存
free(encoded_data);
free(decoded_data);

return 0;
}