Base64编码原理与实现

Base64编码的原理是按bit将每6个bit转换成Base64编码表中的相应字符。下面是Base64的编码表:

0 A 17 R 34 i 51 z
1 B 18 S 35 j 52 0
2 C 19 T 36 k 53 1
3 D 20 U 37 l 54 2
4 E 21 V 38 m 55 3
5 F 22 W 39 n 56 4
6 G 23 X 40 o 57 5
7 H 24 Y 41 p 58 6
8 I 25 Z 42 q 59 7
9 J 26 a 43 r 60 8
10 K 27 b 44 s 61 9
11 L 28 c 45 t 62 +
12 M 29 d 46 u 63 /
13 N 30 e 47 v
14 O 31 f 48 w
15 P 32 g 49 x
16 Q 33 h 50 y

    在转换到最后一个字节时,可能出现如下两种情况:
    1.  最后只剩下2个bit。
    2.  最后只剩下4个bit。
    对于这两种情况,需要在后面被0,如下面的两个字节:

    11011001 11011101

    在转换上面的字节时,最后会剩下4个bit。也就是1101,这时需要在后面补0,也就是变成了110100。如果后面补一对0,转换结果后面加一个 “=”,如果补两对0,加两个“=”,也就是总共的bit数除3的余数为1,则加一个“=”,余数为2,加两个“=”。上面的两个字节是16个bit,除 3的余数是1,因此,需要补一个“=”,也就是将这两个字节分成如下三组:

    110110  011101  110100 

    其中110100后面两个0是补的,因此,查找上面的base64编码表可将这两个字节转换成如下的Base64编码:

    2d0=

    下面我们来实现这个算法。算法的基本原理如下:

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

Code:
  1. public static String encoder(byte[] bytes)  
  2. {  
  3.     StringBuilder result = new StringBuilder();  
  4.     String base64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";  
  5.     //  prevByteBitCount表示从前一个字节取得的bit数,nextByteBitCount表示从后一个字节取得的bit数  
  6.     int prevByteBitCount = 0, nextByteBitCount = 6;  
  7.     //  i表示当前的数组索引,n表示已经处理的位数  
  8.     int i = 0, n = 0;  
  9.     //  byteCount表示总的位数  
  10.     int byteCount = 8 * bytes.length;  
  11.     byte b = 0;  
  12.     while (true)  
  13.     {  
  14.         //  处理从前后两个字节取得位数的情况  
  15.         if (prevByteBitCount > 0 && nextByteBitCount > 0)  
  16.         {  
  17.             // 将前一个字节的低位向左移nextByteBitCount个bit,并使下一个字节的高位(nextByteBitCount指定的位数)右移到字节的最低位,  
  18.             // 然后将两个位移结果进行逻辑或,也就是将从前一个字节和后一个字节取得的相应的bit合并为一个字节的低位  
  19.             b = (byte) (((0xff & bytes[i]) << nextByteBitCount) | ((0xff & bytes[i + 1]) >> (8 - nextByteBitCount)));  
  20.             //  将逻辑或后的结果的最高两个bit置成0  
  21.             b = (byte) (b & 0x3f);  
  22.             prevByteBitCount = 8 - nextByteBitCount;  
  23.             nextByteBitCount = 6 - prevByteBitCount;  
  24.         }  
  25.         //  处理从后一个字节取得高6位的情况  
  26.         else if (prevByteBitCount == 0)  
  27.         {  
  28.             //  后一个字节的高6位右移动低6位  
  29.             b = (byte) ((0xff & bytes[i]) >> (8 - nextByteBitCount));  
  30.             //  处理后面的位时,就是从前一个字节取2个bit,从后一个字字取4个bit  
  31.             prevByteBitCount = 2;  
  32.             nextByteBitCount = 4;  
  33.   
  34.         }  
  35.         //  处理从前一个字节取得低6位的情况  
  36.         else if (nextByteBitCount == 0)  
  37.         {  
  38.             //  将前一个字节的最高两个bit置成0  
  39.             b = (byte) (0x3f & bytes[i]);  
  40.             //  处理后面的位时,从后一个字节取6个bit  
  41.             prevByteBitCount = 0;  
  42.             nextByteBitCount = 6;  
  43.   
  44.         }  
  45.         result.append(base64.charAt(b));  
  46.         n += 6;  
  47.         i = n / 8;  
  48.         int remainBitCount = byteCount - n;  
  49.         if (remainBitCount < 6)  
  50.         {  
  51.             //  将剩余的bit补0后,仍然需要在base64编码表中查找相应的字符,并添加到结果字符串的最后  
  52.             if (remainBitCount > 0)  
  53.             {  
  54.                 b = bytes[bytes.length - 1];  
  55.                 b = (byte) (0x3f & (b << (6 - remainBitCount)));  
  56.                 result.append(base64.charAt(b));  
  57.             }  
  58.             break;  
  59.         }  
  60.     }  
  61.     //  如果总bit数除3的余数为1,加一个“=”,为2,加两个“=”  
  62.     n = byteCount % 3;  
  63.     for (i = 0; i < n; i++)  
  64.         result.append("=");  
  65.   
  66.     return result.toString();  

    最后可以使用下面的代码来验证encoder方法的正确性:

Code:
  1. String s = "*";  
  2. byte[] bytes = s.getBytes("UTF-8");  
  3. System.out.println(encoder(bytes));  
  4. //  使用jdk提供的base64转换类对字节数组进行base64编码  
  5. sun.misc.BASE64Encoder base64Encoder = new sun.misc.BASE64Encoder();  
  6. System.out.println(base64Encoder.encode(bytes));  

    上面的代码的执行结果如下:

5Lit5Y2O5Lq65rCR5YWx5ZKM5Zu9
5Lit5Y2O5Lq65rCR5YWx5ZKM5Zu9
上一篇:创建 macvlan 网络 - 每天5分钟玩转 Docker 容器技术(55)


下一篇:谷歌与 Android 手机厂商签专利共享协议 防止专利诉讼