百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术分析 > 正文

一份简明的 Base64 原理解析

liebian365 2024-10-24 14:34 22 浏览 0 评论

书接上回,在 记一个 Base64 有关的 Bug 一文里,我们说到了 Base64 的编解码器有不同实现,交叉使用它们可能引发的问题等等。

这一回,我们来对 Base64 这一常用编解码技术的原理一探究竟。

1. Base64 是什么

Base64 是一种基于 64 个可打印字符来表示二进制数据的表示方法。由于 2^6=64,所以每 6 个比特为一个单元,对应某个可打印字符。3 个字节有 24 个比特,对应于 4 个 Base64 单元,即 3 个字节可由 4 个可打印字符来表示。

——维基百科

它不是一种加解密技术,是一种简单的编解码技术。

Base64 常用于表示、传输、存储二进制数据,也可以用于将一些含有特殊字符的文本内容编码,以便传输。

比如:

  1. 在电子邮件的传输中,Base64 可以用来将 binary 的字节序列,比如附件,编码成 ASCII 字节序列;
  2. 将一些体积不大的图片 Base64 编码后,直接内嵌到网页源码里;
  3. 将要传递给 HTTP 请求的参数做简单的转换,降低肉眼可读性;注:用于 URL 的 Base64 非标准 Base64,是一种变种。
  4. 网友们在论坛等公开场合习惯将邮箱地址 Base64 后再发出来,防止被爬虫抓取后发送垃圾邮件。

2. Base64 编码原理

标准 Base64 里的 64 个可打印字符是 A-Za-z0-9+/,分别依次对应索引值 0-63。索引表如下:

编码时,每 3 个字节一组,共 8bit*3=24bit,划分成 4 组,即每 6bit 代表一个编码后的索引值,划分如下图所示:

这样可能不太直观,举个例子就容易理解了。比如我们对 cat 进行编码:

可以看到 cat 编码后变成了 Y2F0。

如果待编码内容的字节数不是 3 的整数倍,那需要进行一些额外的处理。

如果最后剩下 1 个字节,那么将补 4 个 0 位,编码成 2 个 Base64 字符,然后补两个 =:

如果最后剩下 2 个字节,那么将补 2 个 0 位,编码成 3 个 Base64 字符,然后补一个 =:

3. 实现一个简易的 Base64 编码器

讲完原理,我们就可以动手实现一个简易的标准 Base64 编码器了,以下是我参考 Java 8 的 java.util.Base64 乱写的一个 Java 版本,仅供参考,主要功能代码如下:

public class CustomBase64Encoder {

    /**
     * 索引表
     */
    private static final char[] sBase64 = {
            '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', '+', '/'
    };

    /**
     * 将 byte[] 进行 Base64 编码并返回字符串
     * @param src 原文
     * @return 编码后的字符串
     */
    public static String encode(byte[] src) {
        if (src == null) {
            return null;
        }

        byte[] dst = new byte[(src.length + 2) / 3 * 4];

        int index = 0;

        // 每次将 3 个字节编码为 4 个字节
        for (int i = 0; i < (src.length / 3 * 3); i += 3) {
            int bits = (src[i] & 0xff) << 16 | (src[i + 1] & 0xff) << 8 | (src[i + 2] & 0xff);
            dst[index++] = (byte) sBase64[(bits >>> 18) & 0x3f];
            dst[index++] = (byte) sBase64[(bits >>> 12) & 0x3f];
            dst[index++] = (byte) sBase64[(bits >>> 6) & 0x3f];
            dst[index++] = (byte) sBase64[bits & 0x3f];
        }

        // 处理剩下的 1 个或 2 个字节
        if (src.length % 3 == 1) {
            int bits = (src[src.length - 1] & 0xff) << 4;
            dst[index++] = (byte) sBase64[(bits >>> 6) & 0x3f];
            dst[index++] = (byte) sBase64[bits & 0x3f];
            dst[index++] = '=';
            dst[index] = '=';
        } else if (src.length % 3 == 2) {
            int bits = (src[src.length - 2] & 0xff) << 10 | (src[src.length - 1] & 0xff) << 2;
            dst[index++] = (byte) sBase64[(bits >>> 12) & 0x3f];
            dst[index++] = (byte) sBase64[(bits >>> 6) & 0x3f];
            dst[index++] = (byte) sBase64[bits & 0x3f];
            dst[index] = '=';
        }

        return new String(dst);
    }
}

这部分源码我也上传到 GitHub 仓库 https://github.com/mzlogin/spring-practices 的 base64test 工程里了。

4. 其它知识点

4.1 为什么有的编码结果带回车

在电子邮件中,根据 RFC 822 规定,每 76 个字符需要加上一个回车换行,所以有些编码器实现,比如 sun.misc.BASE64Encoder.encode,是带回车的,还有 java.util.Base64.Encoder.RFC2045,是带回车换行的,每行 76 个字符。

4.2 Base64 的变种

除了标准 Base64 之外,还有一些其它的 Base64 变种。

比如在 URL 的应用场景中,因为标准 Base64 索引表中的 / 和 + 会被 URLEncoder 转义成 %XX 形式,但 % 是 SQL 中的通配符,直接用于数据库操作会有问题。此时可以采用 URL Safe 的编码器,索引表中的 /+ 被换成 -_,比如 java.util.Base64.Encoder.RFC4648_URLSAFE 就是这样的实现。

5. 参考链接

  • https://zh.wikipedia.org/zh-hans/Base64
  • https://www.liaoxuefeng.com/wiki/897692888725344/949441536192576

相关推荐

4万多吨豪华游轮遇险 竟是因为这个原因……

(观察者网讯)4.7万吨豪华游轮搁浅,竟是因为油量太低?据观察者网此前报道,挪威游轮“维京天空”号上周六(23日)在挪威近海发生引擎故障搁浅。船上载有1300多人,其中28人受伤住院。经过数天的调...

“菜鸟黑客”必用兵器之“渗透测试篇二”

"菜鸟黑客"必用兵器之"渗透测试篇二"上篇文章主要针对伙伴们对"渗透测试"应该如何学习?"渗透测试"的基本流程?本篇文章继续上次的分享,接着介绍一下黑客们常用的渗透测试工具有哪些?以及用实验环境让大家...

科幻春晚丨《震动羽翼说“Hello”》两万年星间飞行,探测器对地球的最终告白

作者|藤井太洋译者|祝力新【编者按】2021年科幻春晚的最后一篇小说,来自大家喜爱的日本科幻作家藤井太洋。小说将视角放在一颗太空探测器上,延续了他一贯的浪漫风格。...

麦子陪你做作业(二):KEGG通路数据库的正确打开姿势

作者:麦子KEGG是通路数据库中最庞大的,涵盖基因组网络信息,主要注释基因的功能和调控关系。当我们选到了合适的候选分子,单变量研究也已做完,接着研究机制的时便可使用到它。你需要了解你的分子目前已有哪些...

知存科技王绍迪:突破存储墙瓶颈,详解存算一体架构优势

智东西(公众号:zhidxcom)编辑|韦世玮智东西6月5日消息,近日,在落幕不久的GTIC2021嵌入式AI创新峰会上,知存科技CEO王绍迪博士以《存算一体AI芯片:AIoT设备的算力新选择》...

每日新闻播报(September 14)_每日新闻播报英文

AnOscarstatuestandscoveredwithplasticduringpreparationsleadinguptothe87thAcademyAward...

香港新巴城巴开放实时到站数据 供科技界研发使用

中新网3月22日电据香港《明报》报道,香港特区政府致力推动智慧城市,鼓励公私营机构开放数据,以便科技界研发使用。香港运输署21日与新巴及城巴(两巴)公司签署谅解备忘录,两巴将于2019年第3季度,开...

5款不容错过的APP: Red Bull Alert,Flipagram,WifiMapper

本周有不少非常出色的app推出,鸵鸟电台做了一个小合集。亮相本周榜单的有WifiMapper's安卓版的app,其中包含了RedBull的一款新型闹钟,还有一款可爱的怪物主题益智游戏。一起来看看我...

Qt动画效果展示_qt显示图片

今天在这篇博文中,主要实践Qt动画,做一个实例来讲解Qt动画使用,其界面如下图所示(由于没有录制为gif动画图片,所以请各位下载查看效果):该程序使用应用程序单窗口,主窗口继承于QMainWindow...

如何从0到1设计实现一门自己的脚本语言

作者:dong...

三年级语文上册 仿写句子 需要的直接下载打印吧

描写秋天的好句好段1.秋天来了,山野变成了美丽的图画。苹果露出红红的脸庞,梨树挂起金黄的灯笼,高粱举起了燃烧的火把。大雁在天空一会儿写“人”字,一会儿写“一”字。2.花园里,菊花争奇斗艳,红的似火,粉...

C++|那些一看就很简洁、优雅、经典的小代码段

目录0等概率随机洗牌:1大小写转换2字符串复制...

二年级上册语文必考句子仿写,家长打印,孩子照着练

二年级上册语文必考句子仿写,家长打印,孩子照着练。具体如下:...

一年级语文上 句子专项练习(可打印)

...

亲自上阵!C++ 大佬深度“剧透”:C++26 将如何在代码生成上对抗 Rust?

...

取消回复欢迎 发表评论: