Base64的Java代碼實現


歡迎拍磚~

在數據二進制和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()方法,也就是說,負數如:10000001
102              * 對Integer.parseInt()來說並不會認為是負數,符號位1被當作數值位,是129
103              * 同時Byte.parseByte()方法里還對數值范圍做了校驗,符號位1,已超出范圍,這樣
104              * 會拋出異常。而Byte又沒有提供toBinaryString的方法
105              * 為了保存byte的二進制值,可利用按位與的方法
106              * 例如有一個負數1000 1111,要把它以字符串保留出來,利用它與1111 1111的與操作,
107              * 再轉成int類型。1000 1111 & 1111 1111
108              * 在內存中保存的就是 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這樣的數值認為是正數

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM