Base64

基于64个可打印字符来表示二进制数据的表示方法

二进制数据,每个字符取值范围[0, 255],作为ascii码解析时,只有部分可显示(打印)

肉眼查看/作为文本拷贝这份数据,16进制优于二进制ascii码格式

但16进制表示法需两个字节才能表示表示原始数据的一个字节,即大小增加了一倍

base64算法保持编码后可打印特性的同时,大小只增加 1/3

算法原理

log₂64 = 6

每6个比特为一个单元,对应某个可打印字符。3个字节=24个比特,对应于4个Base64单元,即3个字节可由4个可打印字符来表示

解码时将每个可打印的字符按映射表对应,每4个字节看成一个单位,按位运算还原出原始3个字节

原始数据长度不是3的倍数?

一种是最后剩1个字节,另一种是剩两个字节:

  • 剩余1个字节时,1个字节编码成2个字节,剩余2个字节填充为 ==
  • 剩余2个字节时,2个字节编码成3个字节,剩余1个字节填充为 =

即base64保证了编码后的字符串长度为4的倍数

URL-safe variant

标准 Base64 编码字符集中包含 +, /, =,在 URL 中使用时需要特别处理。为避免对 URL 造成干扰,RFC 4648Base64 URL-safe variant 提出了将:

  • + 替换为 -
  • / 替换为 _
  • =(填充符)通常省略

Java 示例:使用 java.util.Base64

import java.util.Base64;

public class Base64UrlExample {
    public static void main(String[] args) {
        String original = "hello/world?key=1+2=";

        // 编码
        String encoded = Base64.getUrlEncoder().withoutPadding().encodeToString(original.getBytes());
        System.out.println("URL-safe Base64 Encoded: " + encoded);

        // 解码
        byte[] decodedBytes = Base64.getUrlDecoder().decode(encoded);
        String decoded = new String(decodedBytes);
        System.out.println("Decoded: " + decoded);
    }
}

输出示例:

vbnet复制编辑URL-safe Base64 Encoded: aGVsbG8vd29ybGQ_a2V5PTErMj0
Decoded: hello/world?key=1+2=

Go 示例:使用 encoding/base64

package main

import (
	"encoding/base64"
	"fmt"
)

func main() {
	original := "hello/world?key=1+2="

	// 编码
	encoded := base64.RawURLEncoding.EncodeToString([]byte(original))
	fmt.Println("URL-safe Base64 Encoded:", encoded)

	// 解码
	decodedBytes, err := base64.RawURLEncoding.DecodeString(encoded)
	if err != nil {
		panic(err)
	}
	fmt.Println("Decoded:", string(decodedBytes))
}

输出示例:

vbnet复制编辑URL-safe Base64 Encoded: aGVsbG8vd29ybGQ_a2V5PTErMj0
Decoded: hello/world?key=1+2=

Base58

Bitcoin中使用的一种独特的编码方式,主要用于产生Bitcoin的钱包地址。相比Base64,Base58不使用数字"0",字母大写"O",字母大写"I",和字母小写"l",以及"+“和”/“符号

设计Base58主要的目的是:

  • 避免混淆。在某些字体下,数字0和字母大写O,以及字母大写I和字母小写l会非常相似

  • 不使用 + 和 / 的原因是非字母或数字的字符串作为帐号较难被接受

  • 没有标点符号,通常不会被从中间分行

  • 大部分的软件支持双击选择整个字符串