什么是 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 编码相关推荐

  1. mysql base64 加密解密_烂泥:base64加密与解密

    本文由ilanniweb微信公众号提供友情赞助,首发于烂泥行天下 jenkins技术分享QQ群:571981257 一.什么是base64 base64是网络上最常见的用于传输8Bit字节码的编码方式 ...

  2. 【后端过程记录】用flask搭建服务器作后端接收数据 将base64字符串码解码为可读取文件 载入训练好的模型进行预测

    因为项目的原因了解到有一个python的flask框架,查了一下: 关于前端图片上传的canvas: 如下元素 <canvas id="canvas" width=" ...

  3. 签名算法sha256withrsa,RSA数字证书公钥私钥生成,base64转码和文件日志

    RSA数字证书公钥密钥生成: 在Linux系统下运行以下命令生成: 如果提示输出密码,可以为空,直接回车 生成的公钥rsa_public_key.pem和密钥rsa_private_key.pem文件 ...

  4. javascript使用btoa和atob来进行Base64转码和解码

    javascript中如何使用Base64转码 let str = 'javascript';let btoaStr = window.btoa(str); //转码结果 amF2YXNjcmlwdA ...

  5. Android数据传输加密(一):Base64转码算法

    前言:本文重点在第4部分,Android中Base64算法的使用,主要是介绍android.util.Base64类,其他为对Base64原理的讲解,不关心原理的小伙伴,可直接阅读第4部分 1.何为B ...

  6. Base64转码过程

    base64 是一个转码技术.专门用来应对二进制转成可视字符,这种场景. 要学习base64转码过程,需要先了解ACSII码表. 有了ASCII码表还需要有base64编码表 base64转码规则: ...

  7. 生成带有红黄绿码的二维码,并转base64

    生成带有红黄绿码的二维码,并转base64 一.依赖(pom) <dependency><groupId>com.google.zxing</groupId>< ...

  8. 基于vue+springboot,实现图片base64转码保存到数据库

    基于vue+springboot,实现图片base64转码保存到数据库 背景:在项目中遇到一个新增.编辑的场景.新增一个车辆信息,并且附有车辆照片和关联的设备照片,照片转码成base64保存到mysq ...

  9. base64转码java版

    base64转码java版 package com.net.util;import java.io.FileInputStream; import java.io.FileOutputStream; ...

  10. 直接用img 的src属性显示base64转码后的字符串成图片【原】

    直接用<img> 的src属性显示base64转码后的字符串成图片 <img src="data:image/gif;base64,base64转码后的字符串" ...

最新文章

  1. LR为什么要先离散化
  2. 《C语言程序设计:问题与求解方法》——3.8节不同类型数据之间的类型转换
  3. SpringMVC应用和RESTful应用的区别
  4. pwn学习总结(一) —— 常用命令
  5. sublime text 自动保存
  6. ICMP:Internet控制报文协议
  7. 误码率越高越好还是越低越好_夜间护理步骤越多越好还是越少越好?NFF
  8. Java反编译器Java Decompiler
  9. 后台系统可扩展性学习笔记(十四)异步机制与MQ
  10. TI基于MSP430F67641的电能表技术方案
  11. linux sdl 显示内存,Linux系统:如何解决sdl安装遇到的问题
  12. FastReport浏览器直接打印无须预览(2023终版)
  13. Git命令行和Puttygen生成公钥私钥的方法和区别
  14. 网站被黑客攻击怎么办?
  15. 1483选票统计(一)(结构体专题)
  16. 人工智能进化史:从麦卡锡到“索菲亚” | 精选
  17. 5G 产业链重要细分投资领域
  18. python-电脑调用手机摄像头
  19. wml 与服务器交互
  20. 查询,珍爱网ID,百合网ID,世纪佳缘ID,有缘网ID

热门文章

  1. “四大神兽”拆机指北
  2. C# 压缩和修复Access数据库
  3. Paddle-Lite 安卓端部署
  4. Golang hijack 劫持
  5. PostgreSQL安装(绿色版)
  6. 三国志战略版:Daniel_平民掐大佬之《兵法九章》
  7. 关于英伟达显卡驱动程序(GeForce)无法下载的问题
  8. oracle数据库02195,数据库表空间操作 - osc_w33tzsln的个人空间 - OSCHINA - 中文开源技术交流社区...
  9. leetcode:买卖股票的最佳时机2(python)
  10. Nginx网页优化(隐藏版本号,日志分割,更改进程数,网页压缩,防盗链详