想必大家在项目开发中或多或少的接触过base64编码,说它是一种加密方式,未免太过了。其实就是一种为了防止被肉眼直接看到而做的一种转码。
下面来说一下base64编码的原理,有三个字节转换成四个字节,假如一个字节数组是长度是3,如下:
01110010 11001010 00110111 一共24个bit,每六个一组,前面补充00
这3个字节转换后变成 00011100 00101100 00101000 00110111
根据以上原理,就可以把一个字节数组转换成一个 每个字节的值在 0~63 (0000000 ~ 00111111)之内的字节数组。
接下来就是根据这个字节的值当做一个下标,寻找此下标对应的字母或者符号,恰好是64种下标。对于字节不足的,不能被3整除的字节数组,末尾补充一个不在这个64种符号或者字母之内的符号,解码时去掉这个特殊符号。
package com.ryx.main;
/**
*
* @author lq
*
*/
public class Base64New {
private static final int BASE = 3;
private static final int EXPAND = 4;
private static final int SIGN = -128;
private static final char END = '$';
//base64的基准字符,可以自定义,不过最好是可打印字符
private static final String str = "<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{";
public static String encodeBase64Str(String str){
return new String(encodeBase64(str));
}
public static byte[] encodeBase64(String str){
return encodeBase64(str.getBytes());
}
public static byte[] encodeBase64(byte[] data){
int len = data.length;
int overBytes = len%BASE;
int lenBytes = len/BASE;
int encodeBytesLen = 0;
if(overBytes == 0){
encodeBytesLen = lenBytes*EXPAND;
}else{
encodeBytesLen = (lenBytes+1)*EXPAND;
}
byte[] encodeBytes = new byte[encodeBytesLen];
int step = 0;
int encodeIndex = 0;
byte b1,b2,b3,m0,m1;
for(int i=0;i<lenBytes;i++){
step = i*3;
//取出三个字节
b1 = data[step];
b2 = data[step+1];
b3 = data[step+2];
//获取第一个字节的最后两位
m0 = (byte) (b1&0x03);
//获取第二个字节的最后四位
m1 = (byte) (b2&0x0f);
// 字节&-128,非负数都为0,负数的话把高位11转换成00
byte b1Index = (byte)((b1&SIGN)==0?b1>>2:b1>>2^0xc0);
byte b2Index = (byte)((b2&SIGN)==0?b2>>4:b2>>4^0xf0);
byte b3Index = (byte)((b3&SIGN)==0?b3>>6:b3>>6^0xfc);
//根据字节的数值当做下标,寻找对应的字符
encodeBytes[encodeIndex]= (byte) str.charAt(b1Index);
encodeBytes[encodeIndex+1]= (byte) str.charAt(b2Index|(m0<<4));
encodeBytes[encodeIndex+2]= (byte) str.charAt(b3Index|(m1<<2));
encodeBytes[encodeIndex+3]= (byte) str.charAt(b3&0x3f);
encodeIndex+=4;
}
//当剩下一个字节,或者两个字节时,需要做的处理
if(overBytes == 1){
b1 = data[len-1];
byte b1Index = (byte)((b1&SIGN)==0?b1>>2:b1>>2^0xc0);
m0 = (byte) (b1&0x03);
encodeBytes[lenBytes*EXPAND]= (byte) str.charAt(b1Index);
encodeBytes[lenBytes*EXPAND+1]= (byte) str.charAt((m0<<4));
encodeBytes[lenBytes*EXPAND+2]= END;
encodeBytes[lenBytes*EXPAND+3]= END;
}else if(overBytes == 2){
b1 = data[len-2];
b2 = data[len-1];
byte b1Index = (byte)((b1&SIGN)==0?b1>>2:b1>>2^0xc0);
byte b2Index = (byte)((b2&SIGN)==0?b2>>4:b2>>4^0xf0);
m0 = (byte) (b1&0x03);
m1 = (byte) (b2&0x0f);
encodeBytes[lenBytes*EXPAND]= (byte) str.charAt(b1Index);
encodeBytes[lenBytes*EXPAND+1]= (byte) str.charAt(b2Index|(m0<<4));
encodeBytes[lenBytes*EXPAND+2]= (byte) str.charAt((m1<<2));
encodeBytes[lenBytes*EXPAND+3]= END;
}
return encodeBytes;
}
//根据上面的逻辑反推解码,应该很简单了
public static byte[] decodeBase64(byte[] data){
if(data.length == 0){
return new byte[0];
}
int len = 0;
if((len = data.length%EXPAND)!=0){
throw new IllegalArgumentException("data is not base 64 bytes");
}
len = data.length/EXPAND;
byte[] decodeBytes = new byte[len*BASE];
int byteLen = 0;
int decodeIndex = 0;
byte b1,b2,b3,b4;
for(int i=0;i<len;i++){
decodeIndex = i*4;
b1 = data[decodeIndex];
b2 = data[decodeIndex+1];
b3 = data[decodeIndex+2];
b4 = data[decodeIndex+3];
if(b3!=END&&b4!=END){
//无补充符号
b1 = (byte) str.indexOf(b1);
b2 = (byte) str.indexOf(b2);
b3 = (byte) str.indexOf(b3);
b4 = (byte) str.indexOf(b4);
decodeBytes[byteLen++] = (byte) (b1<<2|b2>>4);
decodeBytes[byteLen++] = (byte) (((b2&0xf)<<4)|((b3>>2)&0xf));
decodeBytes[byteLen++] = (byte) (b3<<6|b4);
}else if(b3==END){
b1 = (byte) str.indexOf(b1);
b2 = (byte) str.indexOf(b2);
decodeBytes[byteLen++] = (byte) (b1<<2|b2>>4);
}else if(b4==END){
b1 = (byte) str.indexOf(b1);
b2 = (byte) str.indexOf(b2);
b3 = (byte) str.indexOf(b3);
decodeBytes[byteLen++] = (byte) (b1<<2|b2>>4);
decodeBytes[byteLen++] = (byte) (((b2&0xf)<<4)|((b3>>2)&0xf));
}
}
byte[] retBytes = new byte[byteLen];
System.arraycopy(decodeBytes, 0, retBytes, 0, byteLen);
return retBytes;
}
public static byte[] headAndTailEncodeBase64(String str){
return headAndTailEncodeBase64(str.getBytes());
}
//头尾字节交叉后的编码处理
public static byte[] headAndTailEncodeBase64(byte[] data){
int len = data.length;
if(len==0){
return new byte[0];
}
int half = data.length/2;
int over = data.length%2;
byte[] newByte = new byte[len];
for(int i=0;i<half;){
newByte[i*2] = data[i];
newByte[((++i)*2-1)] = data[len-i];
}
if(over!=0){
newByte[len-1] = data[half];
}
return encodeBase64(newByte);
}
public static byte[] headAndTailDecodeBase64(byte[] data){
byte[] ret = decodeBase64(data);
if(ret.length==0){
return new byte[0];
}
int len = ret.length;
int half = ret.length/2;
int over = ret.length%2;
byte[] newByte = new byte[len];
for(int i=0;i<half;){
newByte[i] = ret[i*2];
newByte[len-(++i)] = ret[(i*2-1)];
}
if(over!=0){
newByte[half] = data[len-1];
}
return newByte;
}
public static void main(String[] args) {
byte[] data = "你好哈".getBytes();
System.out.println(new String(headAndTailEncodeBase64(data)));
System.out.println(new String(headAndTailDecodeBase64(headAndTailEncodeBase64("你好哈".getBytes()))));
}
}
想弄明白base64,或者想定义自己的base64,了解原理之后,最好知道这三个运算符的作用 & | ^ ,对base64算法就清楚了,还有记住编码时,是把字节当下标寻找对应的字符,解码时根据字符寻找对应的下标,剩下的就看自己怎么去处理字节了,最后两个编解码方法是一个头尾字节交叉后的编解码算法。
了解此算法,可以定义自己的base128编码都不是问题。
小弟第一次写博客,希望读者给出意见,版面估计很难看。