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

今天把之前实现的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个编码后的字节。

        @Override
	public byte firstByte(byte b) {
		//对字节b右移2位
		int r_f=b & 0xff;
		r_f=r_f >>> 2;
		return (byte)(r_f & 0x3f);
	}

	@Override
	public 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);
	}

	@Override
	public 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);
	}

	@Override
	public 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 []中对应的值。
@Override
	public 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);
	}

@Override
	public 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();
	}
以上整个编码函数的实现就完成了,对于解码的实现下面直接给出代码。
	@Override
	public byte baseIndex(byte b) {
		for (int i = 0; i < base.length; i++) {
			if (base[i] == b) {
				return (byte) i;
			}
		}
		return -1;
	}

	@Override
	public 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);
	}

	@Override
	public 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);
	}

	@Override
	public 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);
	}

	@Override
	public 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);
	}

	@Override
	public 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
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值