自定义Base64编码和解码的实现
2015-02-17 18:13
190 查看
今天把之前实现的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=。对于这两种特殊情况,在编程时特殊考虑。
现在我们来考虑编码接口设计(解码与之类似):
下面先是实现firstByte,secondByte,thirdByte,fourthByte这四个函数,这四个函数就是实现对3个字节的移位运算,来得到4个编码后的字节。
编码和解码是以三个字符为一组来处理的,对于字符串长度不是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(); }
相关文章推荐
- C#实现Base64编码与解码 自定义
- C#实现Base64编码与解码(二)
- C# 实现Base64的编码解码
- C#实现Base64编码与解码(转)
- JavaScript实现的Base64编码和解码
- C#实现Base64编码与解码
- Base64编码解码的实现(C语言)
- JavaScript Base64编码和解码,实现URL参数传递。
- Base64编码解码与实现
- JavaScript实现的Base64编码和解码
- [导入]JavaScript实现的Base64编码和解码
- C#实现Base64编码与解码
- C#实现Base64编码与解码(二)
- Base64编码与解码的java实现
- JavaScript Base64编码和解码,实现URL参数传递。
- JavaScript实现的Base64编码和解码
- [转贴]JavaScript Base64编码和解码,实现URL参数传递。
- Symbian Base64编码与解码的简单实现
- Base64编码、解码的实现
- Base64编码、解码的实现