• 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这样的数值认为是正数

  • 相关阅读:
    nacos配置文件优先级
    springboot配置文件优先级(由高到低)
    设计模式七大设计原则
    docker安装mycat并实现mysql读写分离和分库分表
    docker部署mysql主从复制
    ideaui中java抬头
    数据定时备份
    docker容器安装vim
    JMeter的安装
    VUE项目中同时使用API代理与MockJs
  • 原文地址:https://www.cnblogs.com/kingsleylam/p/5011718.html
Copyright © 2020-2023  润新知