自定义Base64编码和解码的实现

来源:互联网 发布:凸优化理论与应用 编辑:程序博客网 时间:2024/05/01 01:45

今天把之前实现的Base64的过程写出来,这篇文章的思路是这样的,首先使用自然语言及编程语言来描述Base64编码的过程,然后设计编码和解码的接口函数,最后是代码的实现和测试。


编码和解码是以三个字符为一组来处理的,对于字符串长度不是3的倍数的情况在后面再单独介绍。

例子:s13

编码过程:

      1.获得每个字符对应的ASCII码值的二进制形式:01110011   00110001   00110011;

      2.将上面的3组8位二进制划分成四组6位二进制的形式:011100  110011   000100   110011

      3.高位补零得到4组8位的二进制的形式:00011100   00110011   00000100  00110011

      4.根据这四组二进制所对应的十进制值到编码表中索引出字符:c  z  E z。

上述编码过程中需要我们实现的有:将3组8位二进制转换成4组6位二进制,再高位补0;到编码表中索引出对应的字符。

01110011   00110001   00110011截取第一个字节的前6位得到011100,然后第一个字节的最后两位和第二个字节的前四位组合而成110011,再将第二个字节的后4位和第三个字节的前两位组合成000100,最后由第三个字节的后6位得到110011。


先复习下java中移位运算符的知识:<<(左移),>>(带符号右移)和>>>(无符号右移)。

移位运算符是在二进制的基础上对数字进行平移的,由于移动的方向和填充方式的不同分为上面3种。

举个例子:00110011,对应左移2位:00110011<<2,得到11001100,在其低位补0即可。

>>(带符号右移)的运算规则是:低位舍弃,高位补符号位,即正数补0,负数补1。>>>(无符号右移)就是右移对应位数,低位舍弃,高位全部补0。


下面用编程语言来描述编码过程:

       1.s13对应的字节为byte[0]:01110011,byte[1]:00110001,byte[2]:00110011。

       2.byte[0]无符号右移2位,byte[0]>>>2得到编码后的第一个字节;

       3.byte[0]与0x03(00000011)按位与&,这样高6位全为0,再左移4位,将byte[1]>>>4,最后将byte[0]和byte[1]按位或|,得到编码后第二个字节;

       4.将byte[1]与0x0f(00001111)按位与&,这样byte[1]的高4位就为0了,这样做的目的是byte[1]的高四位我们现在不需要,然后左移2位,将byte[2]>>>6,因为我们需要byte[2]的低2位。最后将byte[1]和byte[1]进行按位或|操作,得到编码后的第三个字节。

       5.将byte[2]的后6位截取,即byte[2]与0x3f按位与&,这样就得到了编码后的第四个字节。

       6.根据这四个字节对应的值到编码转换表中查找其所对应的字符。

对于特殊情况:字符串的长度不是3的倍数,剩余1或2个字符。采取的做法是:若剩余1个字符,将该字符转换出2个,若剩余2个字符,将该字符转换出3个,不够补0,最后用=来填充满4个字符。

举个例子:s 对应的二进制为01110011,截取高6位得到00011100,然后最后两位再补零得到一个字节110000,高位补0,即00110000然后补两个=,所以s编码后为cw==。

ss对应的二进制为01110011  01110011,按照之前的规则得到011100  110111  ,0011再通过补0,得到001100,然后填充=,所以ss编码后为c3M=。对于这两种特殊情况,在编程时特殊考虑。


现在我们来考虑编码接口设计(解码与之类似):

public interface Base64 {/** * 将要编码的字符串的字节编码后返回编码后得到字符串 *  * @param b:要编码的字符串字节数组 * @return 编码后的字符串 */public abstract String encode(byte[] b);/** * 将三个字节中的第一个截取到前6位,得到编码后的第一字节 *  * @param b:三个字节中的第一个 * @return 编码后的第一字节 */public abstract byte firstByte(byte b);/** * 联合三个字节中的第一个和第二个得到编码后的第二个字节 *  * @param last_b:第一个字节    next_b:第二个字节 * @return  编码后的第二个字节 */public abstract byte secondByte(byte last_b,byte next_b);  /** * 联合三个字节中的第二个和第三个得到编码后的第三个字节 *  * @param last_b:第二个字节   next_b:第三个字节 * @return 编码后的第三个字节 */public abstract byte thirdByte(byte last_b,byte next_b);/** * 将三个字节中的第三个字节截取后6位,得到编码后的第四个字节 *  * @param b:第三个字节 * @return 编码后的第四个字节 */public abstract byte fourthByte(byte b);/** * 处理特殊情况:字符串长度%3!=0 *  */public abstract byte lastOneByte(byte b,int move);}
以上函数之间的关系是:外界通过调用encode函数来完成编码,在encode函数内部:首先考虑是否出现特殊情况(字符串长度不是3的倍数),若未出现,就以3个字节为一组来处理字符串:依次调用firstByte,secondByte,thirdByte,fourthByte来得到编码后的四个字节,最后将其转换成编码表中对应的字符即可。若出现特殊情况,在每3个字节为一组在处理完之后,通过lastOneByte来处理剩余的1个或2个字节。


下面先是实现firstByte,secondByte,thirdByte,fourthByte这四个函数,这四个函数就是实现对3个字节的移位运算,来得到4个编码后的字节。

        @Overridepublic byte firstByte(byte b) {//对字节b右移2位int r_f=b & 0xff;r_f=r_f >>> 2;return (byte)(r_f & 0x3f);}@Overridepublic byte secondByte(byte last_b, byte next_b) {//取last_b的低2位和next_b的高4位int r_l=last_b & 0xff;int r_n=next_b & 0xff;r_l=last_b & 0x03;//last_b去掉高6位r_l=r_l << 4;//last_b左移4位r_n=r_n >>> 4;//next_b右移4位return (byte)((r_l | r_n) & 0x3f);}@Overridepublic byte thirdByte(byte last_b, byte next_b) {//取last_b的低4位和next_b的高2位int r_l=last_b & 0xff;int r_n=next_b & 0xff;r_l=r_l & 0x0f;//last_b去掉高4位r_l=r_l << 2;//last_b左移2位r_n=r_n >>> 6;//next_b右移6位return (byte)((r_l | r_n) & 0x3f);}@Overridepublic byte fourthByte(byte b) {//取b的低6位int r_b=b & 0xff;r_b=r_b << 2;r_b=r_b >>> 2;return (byte)(r_b & 0x3f);}
我们通过firstByte,secondByte,thirdByte,fourthByte这四个函数得到的是四个索引,对应到编码转换表中才得到了编码后的字符。所以我们需要先把编码转换表在代码中实现,下面我们使用一个字节数组来存放字符的ASCII码的二进制。
private static final byte base[] = { 0x41, 0x42, 0x43, 0x44, 0x45, 0x46,0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51,0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x61, 0x62,0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d,0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,0x79, 0x7a, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,0x39, 0x2b, 0x2f };
在encode函数中,根据firstByte,secondByte,thirdByte,fourthByte得到的值找到byte []中对应的值。
@Overridepublic byte lastOneByte(byte b, int move) {int r_b = b & 0xff;r_b = r_b << move;r_b = r_b >>> 2;return (byte) (r_b & 0x3f);}

@Overridepublic String encode(byte[] b) {StringBuffer sb = new StringBuffer();int len = b.length;int more_len = len % 3;int use_len = len - more_len;byte[] bytes = new byte[4];for (int i = 0; i < use_len; i += 3) {bytes[0] = base[firstByte(b[i])];bytes[1] = base[secondByte(b[i], b[i + 1])];bytes[2] = base[thirdByte(b[i + 1], b[i + 2])];bytes[3] = base[fourthByte(b[i + 2])];sb.append(new String(bytes));}if (more_len == 1) {byte b_2[] = new byte[2];b_2[0] = base[firstByte(b[len - 1])];b_2[1] = base[lastOneByte(b[len - 1], 6)];sb.append(new String(b_2));return sb.append("==").toString();} else if (more_len == 2) {byte b_3[] = new byte[3];b_3[0] = base[firstByte(b[len - 2])];b_3[1] = base[secondByte(b[len - 2], b[len - 1])];b_3[2] = base[lastOneByte(b[len - 1], 4)];sb.append(new String(b_3));return sb.append("=").toString();}return sb.toString();}
以上整个编码函数的实现就完成了,对于解码的实现下面直接给出代码。
@Overridepublic byte baseIndex(byte b) {for (int i = 0; i < base.length; i++) {if (base[i] == b) {return (byte) i;}}return -1;}@Overridepublic byte backLastOne(byte last_b, byte next_b, int move_l, int move_b) {int r_l = last_b & 0xff;int r_n = next_b & 0xff;r_l = r_l << move_l;r_n = r_n << move_b;r_n = r_n >>> move_b;return (byte) ((r_l | r_n) & 0xff);}@Overridepublic byte backFirst(byte first, byte second) {int r_f = first & 0xff;int r_s = second & 0xff;r_f = r_f << 2;r_s = r_s >>> 4;return (byte) ((r_f | r_s) & 0xff);}@Overridepublic byte backSecond(byte second, byte third) {int r_s = second & 0xff;int r_t = third & 0xff;r_s = r_s << 4;r_t = r_t >>> 2;return (byte) ((r_s | r_t) & 0xff);}@Overridepublic byte backThird(byte third, byte fourth) {int r_t = third & 0xff;int r_f = fourth & 0xff;r_t = r_t << 6;return (byte) ((r_t | r_f) & 0xff);}@Overridepublic String backEncode(byte[] b) {StringBuffer sb = new StringBuffer();Vector<Byte> list = new Vector<Byte>();int real_len = b.length;int len = real_len - 2;int more_len = len & 3;int use_len = len - more_len;for (int i = 0; i < use_len; i += 4) {list.add(backFirst(baseIndex(b[i]), baseIndex(b[i + 1])));list.add(backSecond(baseIndex(b[i + 1]), baseIndex(b[i + 2])));list.add(backThird(baseIndex(b[i + 2]), baseIndex(b[i + 3])));}Enumeration e = list.elements();byte bytes[] = new byte[list.size()];int k = -1;while (e.hasMoreElements()) {bytes[++k] = (Byte) e.nextElement();}sb.append(new String(bytes));if (more_len == 2) {byte b_1[] = new byte[1];b_1[0] = backLastOne(baseIndex(b[len - 2]), baseIndex(b[len - 1]),2, 6);sb.append(new String(b_1));}if (more_len == 3) {byte b_2[] = new byte[2];b_2[0] = backFirst(baseIndex(b[len - 3]), baseIndex(b[len - 2]));b_2[1] = backLastOne(baseIndex(b[len - 2]), baseIndex(b[len - 1]),4, 4);sb.append(new String(b_2));}return sb.toString();}





0 0
原创粉丝点击