admin 管理员组

文章数量: 887016


2024年2月24日发(作者:request动词用法)

Base64编解码

一、编码原理

Base64是一种基于64个可打印字符来表示二进制数据的表示方法。由于2的6次方等于64,所以每6个比特为一个单元,对应某个可打印字符。三个字节有24个比特,对应于4个Base64单元,即3个字节需要用4个可打印字符来表示。编码后的数据比原来的数据略长,是原来的4/3倍。它可用来作为电子邮件的传输编码。在Base64中的可打印字符包括字母A-Z、a-z、数字0-9 ,这样共有62个字符,此外两个可打印符号在不同的系统中而不同(Base64de 编码表如下所示)。Base64常用于在通常处理文本数据的场合,表示、传输、存储一些二进制数据。包括MIME的email,email via MIME, 在XML中存储复杂数据.

Base64编码表

Value

Char

Value

Char

Value

Char

Value

Char

0

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

A

B

C

D

E

F

G

I

J

K

L

M

N

O

P

16

17

18

19

20

21

22

24

25

26

27

28

29

30

31

Q

R

S

T

U

V

W

Y

Z

a

b

c

d

e

f

32

33

34

35

36

37

38

40

41

42

43

44

45

46

47

g

h

i

j

k

l

m

o

p

q

r

s

t

u

v

48

49

50

51

52

53

54

56

57

58

59

60

61

62

63

w

x

y

z

0

1

2

3

4

5

6

7

8

9

+

/

H

23

X

39

n

55

二、编码流程

步骤1:将要编码的所有字符都转化成对应的ASCII码。

步骤2:将所有的ASCII码转换成对应的8位长的二进制数。

步骤3:将所得的二进制数从高位到低位开始分成6位一组,最后一组不足六的则补充0

步骤4:将每组二进制数转换成十进制数,然后对照base64的编码表查找得到相应的编码。

注意:1、 要求被编码字符是8bit的,所以须在ASCII编码范围内,u0000-u00ff,中文就不行。

2、如果被编码的字符串中字符的个数为3的倍数,按照上面的步骤即可得到正确的base64编码。但是如果不是3的倍数则要分情况讨论。如果是3的倍数余1,则要在编好的码字后面加上两个“=”,如果是3的倍数余2,这要在编好的码字后面加上一个“=”。(例如w的base64编码为dw==,w1的base64编码为dzE=)

下面我们来对具体的字符串进行编码举例,以便更好的理解编码流程:

编码「Man」

文本

ASCII编码

索引

Base64编码

M

77

19

T

22

W

a

97

5

F

n

110

46

u

二进制位 0 1 0 0 1 1 0 1 0 1 1 0 0 0 0 1 0 1 1 0 1 1 1 0

在此例中,Base64算法将三个字符编码为4个字符

特殊情况

文本(1 Byte) A

二进制位

Base64编码

文本(2 Byte)

二进制位

Base64编码

A的编码为QQ= =

BC的编码为QkM=

0 1 0 0 0 0 0 1

Q

B

Q

C

二进制位(補0) 0 1 0 0 0 0 0 1 0 0 0 0

0 1 0 0 0 0 1 0 0 1 0 0 0 0 1 1 x x x x x x

Q k M

二进制位(補0) 0 1 0 0 0 0 1 0 0 1 0 0 0 0 1 1 0 0 x x x x x x

三、核心算法程序

算法的基本原理如下:由于每次转换都需要6个bit,而这6个bit可能都来自一个字节,也可以来自前后相临的两个字节。定义两个变量:prevByteBitCount和nextByteBitCount,这两个变量分别表述从前一个和后一个节字取得的bit数。如果prevByteBitCount为0,表示6个bit全部来自下一个字节的高6位。如果nextByteBitCount = 0,表示6个bit全部来自前一个字节的低6位。最后通过适当的移位获得所需要的6个bit,再在上面的base64编码表中查找相应的字符。算法的实现代码如下:

public static String encoder(byte[] bytes)

{

StringBuilder result = new StringBuilder();

String base64 =

"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/";

// prevByteBitCount表示从前一个字节取得的bit数,nextByteBitCount表示从后一个字节取得的bit数

int prevByteBitCount = 0, nextByteBitCount = 6;

// i表示当前的数组索引,n表示已经处理的位数

int i = 0, n = 0;

// byteCount表示总的位数

int byteCount = 8 * ;

byte b = 0;

while (true)

{

// 处理从前后两个字节取得位数的情况

if (prevByteBitCount > 0 && nextByteBitCount > 0)

{

// 将前一个字节的低位向左移nextByteBitCount个bit,并使下一个字节的高位(nextByteBitCount指定的位数)右移到字节的最低位,

// 然后将两个位移结果进行逻辑或,也就是将从前一个字节和后一个字节取得的相应的bit合并为一个字节的低位

b = (byte) (((0xff & bytes[i]) << nextByteBitCount) | ((0xff & bytes[i + 1]) >> (8

- nextByteBitCount)));

// 将逻辑或后的结果的最高两个bit置成0

b = (byte) (b & 0x3f);

prevByteBitCount = 8 - nextByteBitCount;

nextByteBitCount = 6 - prevByteBitCount;

}

// 处理从后一个字节取得高6位的情况

else if (prevByteBitCount == 0)

{

// 后一个字节的高6位右移动低6位

b = (byte) ((0xff & bytes[i]) >> (8 - nextByteBitCount));

// 处理后面的位时,就是从前一个字节取2个bit,从后一个字字取4个bit

prevByteBitCount = 2;

nextByteBitCount = 4;

}

// 处理从前一个字节取得低6位的情况

else if (nextByteBitCount == 0)

{

// 将前一个字节的最高两个bit置成0

b = (byte) (0x3f & bytes[i]);

// 处理后面的位时,从后一个字节取6个bit

prevByteBitCount = 0;

nextByteBitCount = 6;

}

((b));

n += 6;

i = n / 8;

int remainBitCount = byteCount - n;

if (remainBitCount < 6)

{

// 将剩余的bit补0后,仍然需要在base64编码表中查找相应的字符,并添加到结果字符串的最后

if (remainBitCount > 0)

{

b = bytes[ - 1];

b = (byte) (0x3f & (b << (6 - remainBitCount)));

((b));

}

break;

}

}

// 如果总bit数除3的余数为1,加一个“=”,为2,加两个“=”

n = byteCount % 3;

for (i = 0; i < n; i++)

("=");

return ng();

}

对于程序的理解:这个程序应该只是总程序的一部分,它主要实现了对二进

制代码分成以6位长为一组的分组,考虑了位数不足的情况,个人觉得这个是程序中的难点,所以就把它放进来了。在这个程序中直接调用了查找base64编码表这个函数。

四、解码

Base64的解码原理很简单,其实就是编码的逆过程。

步骤1:将所给的字符在base64编码表中查到相应的数值

步骤2:将数值均转化成6位的二进制数

步骤3:将二进制数从高位到低位的顺序,以8为长度分组。(若所给的base64码结尾没有“=”的情况,即字符数正好是4的倍数。如果有“=”,这不是8的倍数,此时需要将最后的两位或者四位省去。这就可能导致不同的符号可以解码出相同的编码,如下面所示的例子。)

步骤4:将每组的二进制数转换成对应的ASCII代码。这样就可以得到解码后的字符。

举例说明:

解码er==

Base64编码表中对应的值为30 43

每个化成对应6为二进制位 011110 101011

以8位长为一组,多余省去,这可得01111010

则其ASCII码数值为7Ah,对应字符为Z

eq==解码所得结果也为Z。

注意:如果在步骤三中所得的数在ASCII码表中找不到对应数,则会出现解码错误。例如erYT解码得到就是Z!!(这是我在网上的base64解码器中实验得到的结果)

五、感悟

我们觉得Base64的编码、解码原理很简单,但是它的应用范围挺广的,而且它的源代码对于我们来说也不简单(有的部分知道怎么回事,但是不知道怎么用程序来实现)。在这次找编解码的过程中,我们受益良多。一开始找的都是那些很大的音频、视频编解码,像ACC、OGG、H.264等等,基本上都看不懂,只能知道它是干什么的。后来又找到了曼彻斯特编码、AMI编解码,结果有人已经做了,所以最后我们选了Base64编解码。所以这次作业让我们了解了好多以前完全没有接触过的代码,收获颇丰。


本文标签: 编码 字符 字节 表示 对应