对象base64转码_什么是 Base64 编码
什么是 Base64
Base64 是二进制到字符编码的一种方案,将二进制数据使用 ASCII 字符串格式表示,即翻译为基数为 64 的一种表示。每个 Base64 数字表示一个 6 比特的数据。三个字节(共 24 个比特)因此可以被表示为 4 个 Base64 数字。
Base64 常用于处理文本数据的场合,表示、传输、存储一些二进制数据。
Base64 编码表
从 0 到 25 ,也就是从 000000 到 011001,是 ASCII 字符 A - Z
从 26 到 51,也就是从 011010 到 110011,是 ASCII 字符 a - z
从 52 到 61,也就是从 110100 到 111101,是 ASCII 字符 0 - 9
62 (111110)是 ASCII 字符 +
63 (111111)是 ASCII 字符 /
缀词(padding) =
因为 Base64 是每 6 个比特进行一次编码,而现代电脑上的编码值被分为了一个个 8 比特的字节,因此,在 Base64 编码的文本中,每 4 个字符表示三个字节的未编码的文本或数据。这就意味着,当未编码的输入的字节数不是 3 的倍数时,编码输出必须加上缀词来使得输出的长度是 4 的倍数。这个缀词便是 = ,它表明:不再需要更多的比特来进行解码。
= 数量规则如下:若源数据的字节数是 3 的倍数,则不需要加 =
若源数据的字节数除 3 余 1,则加两个 =
若源数据的字节数除 3 余 2,则加一个 =
举个例子来说明 = 的使用:
字符串 "Man" 的 ASCII 编码是 01001101 ,01100001 ,01101110 ,(0x4d,0x61,0x6e)。拆成六个一组,就是 010011 ,010110 ,000101 ,101110 。对应于 Base64 编码表中的 T,W,F,u,故编码后 ASCII 编码的"Man" 对应于 "TWFu"。
字符串 "Ma" 的 ASCII 是 01001101 ,01100001 。拆开得到 010011 ,010110 ,0001 。最后一组还少两个比特,补零得到 000100 。对应于 Base64 编码得到 T,W,E,编码结果为 "TWE="。此处的 = 表明补了两个比特。
字符串 "M" 的 ASCII 是 01001101 ,拆开之,得到 010011 ,01 。最后一组要补四个零,得到 010000 。对应 Base64 得到 T,Q。编码结果为 "TQ==",== 表明补了四个比特。
实际上,缀词 = 并不是必须的,因为缺少的字节可以从编码文本的长度中计算得到。例如 “YW55IGNhcm5hbCBwbGVhc3VyZS4” 这个编码串共有 27 个字符,不是 4 的倍数, 且由 mod(27, 4) = 1 可知它补充了两个比特。通过这些信息就可以知道源文本(或数据)(若为 ASCII 编码)是 “any carnal pleasure.”。但是,如果多个 Base64 编码字符串被连接在一起,= 是必要的,因为需要使用它来区分不同来源的字符串。
Base64 的解码
当对 Base64 进行解码时,四个字符通常会被转化为三个字节。当存在缀词时则可能是两个或一个,一个 = 表明四个字符会被转化为两个字节,两个 = 表明四字符会被转为一个字节。上面的例子便是如此。
如果没有缀词,在对所有的四字符组进行转码后,若还有剩下的字符,剩下的字符数一定小于四。这种情况下,只可能剩下两个或三个字符。若剩下两个字符,则转为一个字节,若为三个则转为两个字节。
一个简单的 C 实现
以下代码在 MINGW 下通过编译并通过了下面的测试。
简便起见,我没用使用 stdint 头文件,所有下面的代码想要正确运行的话,必须满足 int 是 32 位。
编码
要想将一个个字节编码为 Base64,首先需要一张二进制码(000000 - 111111)到 64 个字符的映射表:
char base64_encode_table[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', '+', '/'
};
由于现在的计算机中一个字节是 8 位,而 Base64 编码中每 6 位进行一次编码,在处理过程中若直接使用字节(字符)数组可能不是太方便。考虑到 3 * 8 = 4 * 6,先编写一次性对 3 个字节进行处理的函数会方便不少,而且若最后还剩下 1 或 2 个字节,可以一并处理。
void base64_convert3(char * dststr, unsigned char c1, unsigned char c2, unsigned char c3, int num)
{
// dststr must be big enough to contain four char
// num is the number of byte to convert
int convert = 0;
convert = (c1 << 16) | (c2 << 8) | c3;
for (int i = 0; i < 4; i++)
{
int temp = (convert >> (i * 6)) & 0x3f; //0x3f = 00111111
dststr[3 - i] = base64_encode_table[temp];
}
for (int i = 3; i > num; i--)
{
dststr[i] = '=';
}
}
上面的函数的三个字符参数 c1, c2, c3 是顺序排列的 3 个字节,参数 num 是处理字节的个数。如果 num 的值不为 3,那么 c2 或 c3 的值应该设为 0 来确保得到正确的结果。
这个函数没有对参数的合理性进行检验。
接下来就可以完成编码函数了。
int base64_encoder(char * dststr, char * srcstr, int num, int * dstlength)
{
// num is the number of byte
// dstlength is the number of encoded char, include '='
// dststr must be big enough to carry encoded char
// return non-zero means failed
if (dststr == NULL || srcstr == NULL || num <= 0 || dstlength == NULL)
{
return 1;
}
int cnt = 0;
int i = 0;
for (i = 0; i <= num - 3; i += 3)
{
base64_convert3(dststr + cnt, srcstr[i], srcstr[i + 1], srcstr[i + 2], 3);
cnt += 4;
}
if (i == num)
{
dstlength[0] = cnt;
return 0;
}
else if (i == num - 1)
{
base64_convert3(dststr + cnt, srcstr[i], 0, 0, 1);
cnt += 4;
dstlength[0] = cnt;
return 0;
}
else if (i == num - 2)
{
base64_convert3(dststr + cnt, srcstr[i], srcstr[i + 1], 0, 2);
cnt += 4;
dstlength[0] = cnt;
return 0;
}
else
{
return 1;
}
return 1;
}
该函数做了最低限度的参数检查。dststr 代表编码后字符串的目标位置,srcstr 表示源字符串,num 是源字符串的长度,dstlength 指向一个整型变量,用于存储得到的编码的长度。
解码
与编码相似,解码也需要一张映射表,不过 Base64 只规定了从二进制到字符进行编码,而没有规定字符的编码,至于字符编码是 ASCII 还是其他的比如 UTF-8,EBCDEC 。为了简便起见,这里使用 ASCII。可以使用一个一个 if else 来去掉字符集依赖性,不过也太麻烦了点:
unsigned char base64_decode_table(unsigned char ch)
{
//use ascii code
if (isupper(ch))
{
return ch - 65; // 'A' is 65
}
else if (islower(ch))
{
return ch - 97 + 26; // 'a' is 97
}
else if (isdigit(ch))
{
return ch - 48 + 52; // '0' is 48
}
else if (ch == '+')
{
return 62;
}
else if (ch == '/')
{
return 63;
}
else
{
return 255;
}
}
同样,与编码类似,解码时也可以考虑将四个字符看成一组,这样处理带 = 时更方便:
void base64_convert4(char * dststr, unsigned char c1, unsigned char c2, unsigned char c3, unsigned char c4, int num)
{
// num is the number of char
// dststr must not be null
int convert = 0;
convert = base64_decode_table(c4);
convert = convert | (base64_decode_table(c3) << 6);
convert = convert | (base64_decode_table(c2) << 12);
convert = convert | (base64_decode_table(c1) << 18);
for (int i = 0; i <= num - 2; i++)
{
dststr[i] = convert >> (8 * (2 - i)) & 0xff;
}
}
接下来就可以编写解码函数了,因为要处理使用 = 和不使用 = 的情况,代码的选择支有点多,看起来有点长:
int base64_decoder(char * dststr, char * srcstr, int num, int *dstlength)
{
//the min value of num is 2
if (dststr == NULL || srcstr == NULL || num < 2 || dstlength == NULL)
return 1;
int i;
int cnt = 0;
for (i = 0; i <= num - 8; i += 4)
{
base64_convert4(dststr + cnt, srcstr[i], srcstr[i + 1], srcstr[i + 2], srcstr[i + 3], 4);
cnt += 3;
}
if (i == num - 4)
{
if (srcstr[num - 1] != '=')
{
base64_convert4(dststr + cnt, srcstr[i], srcstr[i + 1], srcstr[i + 2], srcstr[i + 3], 4);
cnt += 3;
dstlength[0] = cnt;
return 0;
}
else
{
if (srcstr[num - 2] == '=')
{
base64_convert4(dststr + cnt, srcstr[i], srcstr[i + 1], 0, 0, 2);
cnt += 1;
dstlength[0] = cnt;
return 0;
}
else
{
base64_convert4(dststr + cnt, srcstr[i], srcstr[i + 1], srcstr[i + 2], 0, 3);
cnt += 2;
dstlength[0] = cnt;
return 0;
}
}
}
else if (i == num - 3)
{
base64_convert4(dststr + cnt, srcstr[i], srcstr[i + 1], srcstr[i + 2], 0, 3);
cnt += 2;
dstlength[0] = cnt;
return 0;
}
else if (i == num - 2)
{
base64_convert4(dststr + cnt, srcstr[i], srcstr[i + 1], 0, 0, 2);
cnt += 1;
dstlength[0] = cnt;
return 0;
}
else
{
return 1;
}
return 1;
}
测试
int main(void)
{
char man[] = "Man";
char trans_man[3][20];
int len[3];
//test encode
for (int i = 0; i < 3; i++)
{
base64_encoder(trans_man[i], man, 3 - i, len + i);
trans_man[i][len[i]] = '\0';
printf("%s\n", trans_man[i]);
}
//test decode
char restore[3][20];
int lenres[3];
for (int i = 0; i < 3; i++)
{
base64_decoder(restore[i], trans_man[i], 4, lenres + i);
restore[i][lenres[i]] = '\0';
printf("%s\n", restore[i]);
}
//test for situation without '='
base64_decoder(restore[0], trans_man[1], 3, lenres);
restore[0][lenres[0]] = '\0';
printf("%s\n", restore[0]);
base64_decoder(restore[0], trans_man[2], 2, lenres);
restore[0][lenres[0]] = '\0';
printf("%s\n", restore[0]);
char myself[] = "include-yy";
char myself_encode[20];
int myself_len;
base64_encoder(myself_encode, myself, 10, &myself_len);
myself_encode[myself_len] = '\0';
printf("\n%s\n", myself_encode);
base64_decoder(restore[0], myself_encode, myself_len, lenres);
restore[0][lenres[0]] = '\0';
printf("%s\n", restore[0]);
return 0;
}
输出结果应该为:
TWFu
TWE=
TQ==
Man
Ma
M
Ma
M
aW5jbHVkZS15eQ==
include-yy
在 Python 中使用 Base64
Python 随处可见,用起来也比 C 方便的多,故学习 Python 的用法肯定还是有用的。
Python 的 base64 模块
base64 模块提供了将二进制数据编码为可打印 ASCII 字符和将编码解码为二进制数据的函数。
base64 模块提供的函数不限于操作 base64,还有 base16,base32 等,不过这里只关注 base64 的编码和解码。
接口函数
base64.b64encode(s, altchars=None)
该函数将类字节对象(bytes-like object)使用 base64 进行编码,并返回编码后的字节。
altchars 必须是长度至少为 2 的类字节对象,这些字符指定了代替 + 和 / 的字符。这就允许该函数生成 URL 安全的 Base64 字符串。
base64.b64decode(s, altchars=None, validate=False)
该函数被编码的字符串解码并返回解码字节。
altchars 必须是长度至少为 2 的类字节对象或 ASCII 字符串,这些字符串指定了用来代替 + 和 / 的字符。
validate 默认为 False ,既不是 base-64 字符也不是备用字母表中的字符会被丢弃。如果 validate 为 True ,非 base64 字符会导致 binascii.Error 。
具体用法
首先,导入 python 的 base64 模块
import base64
创建一个字符串,并使用 encode 方法,将其转化为字节对象:
s = 'include-yy'
s = s.encode()
(encode 方法的默认编码是 'uft-8')
接着使用 base64.b64encode() ,便可得到结果:
base.b64encode(s)
想要解码,则调用 base64.b64decode()
s2 = base.b64encode(s)
base64.b64decode(s2.encode());
参考资料
对象base64转码_什么是 Base64 编码相关推荐
- mysql base64 加密解密_烂泥:base64加密与解密
本文由ilanniweb微信公众号提供友情赞助,首发于烂泥行天下 jenkins技术分享QQ群:571981257 一.什么是base64 base64是网络上最常见的用于传输8Bit字节码的编码方式 ...
- 【后端过程记录】用flask搭建服务器作后端接收数据 将base64字符串码解码为可读取文件 载入训练好的模型进行预测
因为项目的原因了解到有一个python的flask框架,查了一下: 关于前端图片上传的canvas: 如下元素 <canvas id="canvas" width=" ...
- 签名算法sha256withrsa,RSA数字证书公钥私钥生成,base64转码和文件日志
RSA数字证书公钥密钥生成: 在Linux系统下运行以下命令生成: 如果提示输出密码,可以为空,直接回车 生成的公钥rsa_public_key.pem和密钥rsa_private_key.pem文件 ...
- javascript使用btoa和atob来进行Base64转码和解码
javascript中如何使用Base64转码 let str = 'javascript';let btoaStr = window.btoa(str); //转码结果 amF2YXNjcmlwdA ...
- Android数据传输加密(一):Base64转码算法
前言:本文重点在第4部分,Android中Base64算法的使用,主要是介绍android.util.Base64类,其他为对Base64原理的讲解,不关心原理的小伙伴,可直接阅读第4部分 1.何为B ...
- Base64转码过程
base64 是一个转码技术.专门用来应对二进制转成可视字符,这种场景. 要学习base64转码过程,需要先了解ACSII码表. 有了ASCII码表还需要有base64编码表 base64转码规则: ...
- 生成带有红黄绿码的二维码,并转base64
生成带有红黄绿码的二维码,并转base64 一.依赖(pom) <dependency><groupId>com.google.zxing</groupId>< ...
- 基于vue+springboot,实现图片base64转码保存到数据库
基于vue+springboot,实现图片base64转码保存到数据库 背景:在项目中遇到一个新增.编辑的场景.新增一个车辆信息,并且附有车辆照片和关联的设备照片,照片转码成base64保存到mysq ...
- base64转码java版
base64转码java版 package com.net.util;import java.io.FileInputStream; import java.io.FileOutputStream; ...
- 直接用img 的src属性显示base64转码后的字符串成图片【原】
直接用<img> 的src属性显示base64转码后的字符串成图片 <img src="data:image/gif;base64,base64转码后的字符串" ...
最新文章
- LR为什么要先离散化
- 《C语言程序设计:问题与求解方法》——3.8节不同类型数据之间的类型转换
- SpringMVC应用和RESTful应用的区别
- pwn学习总结(一) —— 常用命令
- sublime text 自动保存
- ICMP:Internet控制报文协议
- 误码率越高越好还是越低越好_夜间护理步骤越多越好还是越少越好?NFF
- Java反编译器Java Decompiler
- 后台系统可扩展性学习笔记(十四)异步机制与MQ
- TI基于MSP430F67641的电能表技术方案
- linux sdl 显示内存,Linux系统:如何解决sdl安装遇到的问题
- FastReport浏览器直接打印无须预览(2023终版)
- Git命令行和Puttygen生成公钥私钥的方法和区别
- 网站被黑客攻击怎么办?
- 1483选票统计(一)(结构体专题)
- 人工智能进化史:从麦卡锡到“索菲亚” | 精选
- 5G 产业链重要细分投资领域
- python-电脑调用手机摄像头
- wml 与服务器交互
- 查询,珍爱网ID,百合网ID,世纪佳缘ID,有缘网ID
热门文章
- “四大神兽”拆机指北
- C# 压缩和修复Access数据库
- Paddle-Lite 安卓端部署
- Golang hijack 劫持
- PostgreSQL安装(绿色版)
- 三国志战略版:Daniel_平民掐大佬之《兵法九章》
- 关于英伟达显卡驱动程序(GeForce)无法下载的问题
- oracle数据库02195,数据库表空间操作 - osc_w33tzsln的个人空间 - OSCHINA - 中文开源技术交流社区...
- leetcode:买卖股票的最佳时机2(python)
- Nginx网页优化(隐藏版本号,日志分割,更改进程数,网页压缩,防盗链详