BASE64编码

/**
 * 标准Base64编解码,具体规范请参见相关文档。
 * @author sunlen
 * @version 1.0
 */
public class Base64
{
    /** Base64编码表。*/
    private static char Base64Code[] =
    {
        'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
        'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
        'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
        'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/',
    };
    /** Base64解码表。*/
    private static byte Base64Decode[] =
    {
        -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
        -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, //注意两个63,为兼容SMP,
        -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,63,-1,63, //“/”和“-”都翻译成63。
        52,53,54,55,56,57,58,59,60,61,-1,-1,-1, 0,-1,-1,
        -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14, //注意两个0:
        15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1, //“A”和“=”都翻译成0。
        -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,
        41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1,
    };
    /**
     * 构造方法私有化,防止实例化。
     */
    private Base64() {}
    /**
     * Base64编码。将字节数组中字节3个一组编码成4个可见字符。
     * @param b 需要被编码的字节数据。
     * @return 编码后的Base64字符串。
     */
    public static String encode(byte[] b)
    {
        int code = 0;
        //按实际编码后长度开辟内存,加快速度
        StringBuffer sb =new StringBuffer(((b.length-1)/3)<<2+4);
        //进行编码
        for (int i=0;i<b.length;i++)
        {
            code|=(b[i]<<(16-i%3*8)) & (0xff<<(16-i%3*8));
            if (i%3==2 || i==b.length-1)
            {
                sb.append(Base64Code[(code & 0xfc0000) >>> 18 ]);
                sb.append(Base64Code[(code & 0x3f000)  >>> 12 ]);
                sb.append(Base64Code[(code & 0xfc0)    >>> 6  ]);
                sb.append(Base64Code[ code & 0x3f             ]);
                code=0;
            }
        }
        //对于长度非3的整数倍的字节数组,编码前先补0,编码后结尾处编码用=代替,
        //=的个数和短缺的长度一致,以此来标识出数据实际长度
        if (b.length%3>0)
        {
            sb.setCharAt(sb.length()-1,'=');
        }
        if (b.length%3==1)
        {
            sb.setCharAt(sb.length()-2,'=');
        }
        return sb.toString();
    }
    /**
     * Base64解码。
     * @param code 用Base64编码的ASCII字符串
     * @return 解码后的字节数据
     */
    public static byte[] decode(String code)
    {
        //检查参数合法性
        if (code==null)
        {
            return null;
        }
        int len = code.length();
        if (len%4!=0)
        {
            throw new IllegalArgumentException(
                  "Base64 string length must be 4*n");
        }
        if (code.length()==0)
        {
            return new byte[0];
        }
        //统计填充的等号个数
        int pad = 0;
        if (code.charAt(len-1)=='=')
        {
            pad++;
        }
        if (code.charAt(len-2)=='=')
        {
           pad++;
        }
        //根据填充等号的个数来计算实际数据长度
        int retLen = len/4*3 - pad;
        //分配字节数组空间
        byte[] ret = new byte [retLen];
        //查表解码
        char ch1,ch2,ch3,ch4;
        int i;
        for (i=0;i<len;i+=4)
        {
            int j=i/4*3;
            ch1 = code.charAt(i);
            ch2 = code.charAt(i+1);
            ch3 = code.charAt(i+2);
            ch4 = code.charAt(i+3);
            int tmp = (Base64Decode[ch1]<<18)|(Base64Decode[ch2]<<12)
                    |(Base64Decode[ch3]<<6)|(Base64Decode[ch4]);
            ret[j] = (byte) ((tmp&0xff0000) >> 16);
            if (i<len-4)
            {
                ret[j+1] = (byte) ((tmp&0x00ff00) >> 8);
                ret[j+2] =(byte) ((tmp&0x0000ff));
            }
            else
            {
                if(j+1<retLen)
                {
                    ret[j+1] = (byte) ((tmp&0x00ff00) >> 8);
                }
                if(j+2<retLen)
                {
                    ret[j+2] = (byte) ((tmp&0x0000ff));
                }
            }
        }
        return ret;
    }
}

附录:BASE64编码的原理(节选自http://www.vbzx.net/ArticleView/vbzx_Article_View_1199.asp)

BASE64编码的原理

  BASE64编码 的原理很简单,其方法是,将输入数据流每次取6 bit(每bit代表1位二进制),不足6bit的补0,这样,每3个8位字节将编码为4个6位字节(3×8 → 4×6);不满4个字节的以“=”填充。其实这4个六位字节 仍然是8位,只不过高两位被设置为0。当一个字节只有6位有效时,它的取值空间为0 到 2的6次方减1 即63,也就是说被转换的Base64编码的每一个编码的取值空间为(0~63)。事实上,0~63之间的ASCII码有许多不可见字符,所以应该再做一个映射,映射表(码表)为

  这样就可以将3个8位字节,转换为4个可见字符。 也就是说,转换后的字符串要比原来的长1/3,扩张率为3:4。 举例说明:

  1、当字符串字符个数为3的倍数时;比如字符串“ABC”,其在计算机内存中的十六进制表示为$41、$42、$43,十进制表示为“65”“66”“67”;二进制表示为

01000001  01000010  01000011

   将这三个二进制数依次取6bit,

  010000/01  0100/0010  01/000011

  就转换成了:

  010000  010100  001001  000011

  将这四个二进制数转换成十六制数为:$10,$14,$9,$3,十进制数位为16,20,9,3。对照上面的码表,分别查找出对应的字符为Q,U,J,D。也是就说字符串“ABC”经过BASE64编码后得出“QUJD”。

  这是最简单的情况,即ASCII码字符数刚好可以被3整除。接着继续讨论余数为2、为1的情况。

  2、当余数为2时,比如字符串“ce”,其在内存中十六进制表示为$63,$65;十进制表示表示99,101;二进制表示为

  01100011 01100101 依次取6bit 011000/11 0110/0101

  这时,第3个字符不足6位,在后面补零,也就是0101变成010100。转换结果为

  011000 110110 010100

  这3个二进制数转换成十六制数为$18,$36,$14;十进制数位为24,54,20。对照码表得出结果“Y2U”。编码后的字符个数不足4位,用“=”填充,最后编码得出“Y2U=”。

  3、当余数为1时,比如字符串“{”,其在内存中的十六进制表示为$7B,十进制为123,二进制位表示为

  01111011 依次取6bit 011110/11 

  补0后为 011110/110000  转换结果为011110和110000

  这两个二进制数转换成十六进制数为$1E,$30,十进制数为30,48。对照码表得出结果为“ew”,补上“=”,最后编码得出“ew= =”。

  解码也很简单,是编码的逆过程,即将每个字符对照码表换算成6bit的二进制数,然后重组起来,按8位进行截取,得出原码。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏编程

机器学习之Python基础(二)

标题 类 面向对象 装饰器 1 类 首先举一个创建类的例子 class是声明类的关键字,human是类名,括号里的object是继承的父类(在Python2中如...

203100
来自专栏Java后端技术

这个坑,是时候填上了~

​  这两天,在网上逛的时候,发现了如下的一道面试题,感觉还有蛮有意思的,要是不仔细看还真容易掉到坑里面。第一眼看起来比较绕,所以比较难理解。最终我跳出了这个坑...

8010
来自专栏kevindroid

JNI所需的C语言知识小结

17650
来自专栏carven

浅谈闭包

闭包 – closure, 应该可以说是javascript的一个难点吧, 其实说难也不难, 只是因为没有真正一个权威的人/书去给他一个真正的定义。 不过,学编...

9700
来自专栏余林丰

Java中的Object、T(泛型)、?区别

因为最近重新看了泛型,又看了些反射,导致我对Object、T(以下代指泛型)、?产生了疑惑。 我们先来试着理解一下Object类,学习Java的应该都知道Obj...

274100
来自专栏C/C++基础

C++ explicit禁止单参数构造函数隐式调用

C++中单参数构造函数是可以被隐式调用的,主要有两种情形会隐式调用单参数构造函数: (1)同类型对象的拷贝构造;即用相同类型的其它对象来初始化当前对象。 (...

23250
来自专栏Java帮帮-微信公众号-技术文章全总结

Java基础-day10-基础题-继承;抽象类

Java基础-day10-基础题-继承&抽象类 什么是继承?继承有什么好处? 继承是面向对象最显著的一个特性。继承是从已有的类中派生出新的类,新的类能吸收已有类...

37960
来自专栏函数式编程语言及工具

泛函编程(5)-数据结构(Functional Data Structures)

     编程即是编制对数据进行运算的过程。特殊的运算必须用特定的数据结构来支持有效运算。如果没有数据结构的支持,我们就只能为每条数据申明一个内存地址了,然后使...

21960
来自专栏工科狗和生物喵

【计算机本科补全计划】Java学习笔记(四) 修饰符

正文之前 今天总算是把那个党员谈话给弄完了,三个学弟轮番跟我来聊天,讲自己的入党动机啥的,看到他们就仿佛看到了大一的自己,原来当时面对学长,面对这类事情,会紧张...

34390
来自专栏C/C++基础

Google C++编程风格指南(四)之类的相关规范

类是C++中基本的代码单元,自然被广泛使用。本节列举了在写一个类时要做什么、不要做什么。

11520

扫码关注云+社区

领取腾讯云代金券