Base64的Java代码实现

来源:互联网 发布:小电影网站 知乎 编辑:程序博客网 时间:2024/05/22 02:25
欢迎拍砖~

在数据二进制和byte互相转换的地方方法写得有点挫,不知道有没有更好的方法~

顺便复习了java的一些基础东西,如位操作,原码反码补码

可以在这篇blog里学习到详细的知识点:http://www.cnblogs.com/zhangziqiu/archive/2011/03/30/ComputerCode.html

直接上代码吧,知识点在注释上

编码器:

  1 package jdbc.pro.lin;  2   3 import java.util.HashMap;  4 import java.util.Map;  5   6 public class MyBase64Encoder {  7   8     private static final Map<Integer, Character> INDEX_MAP = new HashMap<Integer, Character>();  9  10     private static final char PADDING_CHAR = '='; 11     static { 12         int index = 0; 13         for (int i = 0; i <= 25; i++) { 14             INDEX_MAP.put(index, (char) ((int) 'A' + i)); 15             index++; 16         } 17  18         for (int j = 0; j <= 25; j++) { 19             INDEX_MAP.put(index, (char) ((int) 'a' + j)); 20             index++; 21         } 22  23         for (int k = 0; k <= 9; k++) { 24             INDEX_MAP.put(index, (char) ((int) '0' + k)); 25             index++; 26         } 27  28         INDEX_MAP.put(index, '+'); 29         index++; 30         INDEX_MAP.put(index, '/'); 31     } 32  33     public static String encode(byte[] bytes) throws Exception { 34         /** 35          * 1.转成二进制的字符串(长度为6的倍数) 36          * 2.获取转义后的字符串  37          * 3.不是4的位数,填充=号 38          */ 39         String binaryString = convertByteArray2BinaryString(bytes); 40         String escapeString = escapeBinaryString(binaryString); 41         return paddingEscapeString(escapeString); 42     } 43  44     private static String convertByteArray2BinaryString(byte[] bytes) { 45  46         StringBuilder binaryBuilder = new StringBuilder(); 47         for (byte b : bytes) { 48             binaryBuilder.append(convertByte2BinaryString(b)); 49         } 50  51         int paddingCount = binaryBuilder.length() % 6; 52         int totalCount = paddingCount > 0 ? binaryBuilder.length() / 6 + 1 53                 : binaryBuilder.length() / 6; 54         int actualLength = 6 * totalCount; 55  56         //百分号后面的-号表示长度不够规定长度时,右填充。否则左填充。 57         return String.format("%-" + actualLength + "s", 58                 binaryBuilder.toString()).replace(' ', '0'); 59     } 60  61     private static String escapeBinaryString(String binaryString) 62             throws Exception { 63         if (null == binaryString || binaryString.isEmpty() 64                 || binaryString.length() % 6 != 0) { 65             System.out.println("error"); 66             throw new Exception("escape binary string error."); 67         } 68  69         StringBuilder escapeBuilder = new StringBuilder(); 70         for (int i = 0; i <= binaryString.length() - 1; i += 6) { 71             String escapeString = binaryString.substring(i, i + 6); 72             int index = Integer.parseInt(escapeString, 2); 73             escapeBuilder.append(INDEX_MAP.get(index)); 74         } 75  76         return escapeBuilder.toString(); 77     } 78  79     private static String paddingEscapeString(String escapeString) { 80         int paddingCount = escapeString.length() % 4; 81         int totalCount = paddingCount > 0 ? escapeString.length() / 4 + 1 82                 : escapeString.length() / 4; 83         int actualCount = 4 * totalCount; 84         return String.format("%-" + actualCount + "s", escapeString).replace( 85                 ' ', PADDING_CHAR); 86     } 87  88     private static String convertByte2BinaryString(byte b) { 89          90         /** 91          * 对于非负数,直接使用Integer.toBinaryString方法把它打印出来 92          */ 93         if (b >= 0) { 94             StringBuilder builder = new StringBuilder(); 95             builder.append(Integer.toBinaryString(b)); 96             return String.format("%08d", Integer.parseInt(builder.toString())); 97         } else { 98             /** 99              * 对于负数,要记住内存保存的是补码。100              * 不能直接使用Byte.parseByte()方法。101              * 因为这个方法最终调的是Integer.parseInt()方法,也就是说,负数如:10000001102              * 对Integer.parseInt()来说并不会认为是负数,符号位1被当作数值位,是129103              * 同时Byte.parseByte()方法里还对数值范围做了校验,符号位1,已超出范围,这样104              * 会抛出异常。而Byte又没有提供toBinaryString的方法105              * 为了保存byte的二进制值,可利用按位与的方法106              * 例如有一个负数1000 1111,要把它以字符串保留出来,利用它与1111 1111的与操作,107              * 再转成int类型。1000 1111 & 1111 1111108              * 在内存中保存的就是 00000000 10001111,这时保存的是一个正整数。但我们不关心整数的正负,109              * 因为我们的目的是要把这串字符串截取出来110              * 再利用Integer.toBinaryString()打印出来。111              * Integer.toBinaryString()对于正数,会将前面的零去掉,如上将打印出1000 1111,这就是我们要的结果。112              */113             int value = b & 0xFF;114             return Integer.toBinaryString(value);115         }116     }117 118 }
View Code

解码器:

  1 package jdbc.pro.lin;  2   3 import java.util.ArrayList;  4 import java.util.Arrays;  5 import java.util.Collections;  6 import java.util.HashMap;  7 import java.util.List;  8 import java.util.Map;  9  10 public class MyBase64Decoder { 11     private static final char PADDING_CHAR = '='; 12  13     private static final Map<Character, Integer> VALUE_MAP = new HashMap<Character, Integer>(); 14     static { 15         int index = 0; 16         for (char i = 'A'; i <= 'Z'; i++, index++) { 17             VALUE_MAP.put(i, index); 18         } 19  20         for (char j = 'a'; j <= 'z'; j++, index++) { 21             VALUE_MAP.put(j, index); 22         } 23  24         for (char k = '0'; k <= '9'; k++, index++) { 25             VALUE_MAP.put(k, index); 26         } 27  28         VALUE_MAP.put('+', index); 29         index++; 30         VALUE_MAP.put('/', index); 31     } 32  33     public static byte[] decode(String base64String) { 34  35         if (null == base64String || base64String.isEmpty()) { 36             return null; 37         } 38         /** 39          * 1.去掉末尾拼凑的=符号 2.转成二进制 3.去掉末尾拼凑的0 4.截取每8位取数 40          */ 41         base64String = removePaddingChar(base64String); 42         String binaryString = getBinaryString(base64String); 43         binaryString = removePaddingNumber(binaryString); 44  45         return convertBinaryString2Bytes(binaryString); 46  47     } 48  49     /** 50      * 删除末尾拼凑的=符号 51      *  52      * @param base64String 53      * @return 54      */ 55     private static String removePaddingChar(String base64String) { 56         int firstPaddingIndex = base64String.indexOf(PADDING_CHAR); 57         return firstPaddingIndex >= 0 ? base64String.substring(0, 58                 firstPaddingIndex) : base64String; 59     } 60  61     /** 62      * 将base64字符串转成二进制字符串 63      *  64      * @param base64String 65      * @return 66      */ 67     private static String getBinaryString(String base64String) { 68         StringBuilder binaryBuilder = new StringBuilder(); 69         for (char c : base64String.toCharArray()) { 70             int value = VALUE_MAP.get(c); 71             binaryBuilder.append(String.format("%6s", 72                     Integer.toBinaryString(value)).replace(" ", "0")); 73         } 74  75         return binaryBuilder.toString(); 76     } 77  78     /** 79      * 二进制字符串中的末尾有一些0是因为不足6的倍数而填充的,需要删除 80      *  81      * @param binaryString 82      * @return 83      */ 84     private static String removePaddingNumber(String binaryString) { 85         int remainder = binaryString.length() % 8; 86  87         binaryString = binaryString.substring(0, binaryString.length() 88                 - remainder); 89  90         return binaryString; 91     } 92  93     private static byte[] convertBinaryString2Bytes(String binaryString) { 94         if (null == binaryString || binaryString.length() % 8 != 0) { 95             System.out.println("binary string not well formatted."); 96             return null; 97         } 98         int size = binaryString.length() / 8; 99         byte[] bytes = new byte[size];100         int arrayIndex = 0;101         for (int i = 0; i <= binaryString.length() - 1; i += 8, arrayIndex++) {102             String byteString = binaryString.substring(i, i + 8);103             /**104              * 非负数,直接使用Byte.parseByte()方法105              */106             if (byteString.startsWith("0")) {107                 bytes[arrayIndex] = Byte.parseByte(byteString, 2);108             } else {109                 /**110                  * 10000000为-128是规定而来的。 -128并没有原码和反码表示。对-128的补码表示[10000000]补111                  * 算出来的原码是[0000 0000]原, 这是不正确的)112                  */113                 if (byteString.equals("1000000")) {114                     bytes[arrayIndex] = (byte) -128;115                     continue;116                 }117                 /**118                  * 其他的负数,就要按照补码的规则来计算 即,原码取反+1=补码 那么,补码-1取反=原码119                  * 注意这都是真值部分的计算,符号位不能变120                  */121                 // 补码122                 String twosComplement = byteString.substring(1);123                 byte twoComplementValue = Byte.parseByte(twosComplement, 2);124 125                 // 反码126                 byte oneComplementValue = (byte) (twoComplementValue - 1);127 128                 /**129                  * 这里用到的是0x7F而不是0xFF。因为oneComplementValue是0开头130                  * 若与1开头的异或,结果为1,而在int中该位不是符号位,会当成数值计算,造成数值错误。131                  * 因此,必须结果是0,即,两个0的异或。132                  */133                 // 真值 8位的计算134                 int trueValue = oneComplementValue ^ 0x7F;135 136                 bytes[arrayIndex] = (byte) (trueValue * (-1));137 138             }139         }140 141         return bytes;142     }143 }
View Code

 

测试代码:

 1 package jdbc.pro.lin; 2  3 import oracle.net.aso.i; 4  5 import org.apache.commons.codec.binary.Base64; 6  7 public class TestMain { 8     public static void main(String[] args) throws Exception { 9         byte[] binaryData = { 0, -2, -128, 127, 1, -1, 3, 4, 89, 45, 0 };10         String thirdPartyBase64String = Base64.encodeBase64String(binaryData);11         String myBase64String = MyBase64Encoder.encode(binaryData);12         System.out.println("ThirdParty encode: " + thirdPartyBase64String);13         System.out.println("MyBase64 encode: " + myBase64String);14 15         System.out.print("ThirdParty decode: ");16         printBytes(Base64.decodeBase64(thirdPartyBase64String.getBytes()));17 18         System.out.print("MyBase64 decode: ");19         printBytes(MyBase64Decoder.decode(thirdPartyBase64String));20         21     }22 23     private static void printBytes(byte[] bytes) {24         for (byte b : bytes) {25             System.out.print(b);26             System.out.print(" ");27         }28         System.out.println("");29     }30 }
View Code

 

测试结果:

附Byte.parseByte()的反编译源码,确实是调用了Integer.parseInt()方法,导致符号位失效,对于10001111这样的数值认为是正数

 

0 0