base64编码的实现

  • 什么是base64
  • 进制转换关系
  • 十进制转二进制
  • 十进制转八进制
  • 十进制转十六进制
  • 其他进制转10进制
  • 十六进制转二进制
  • 关于数据在内存中的大小端模式
  • Python将字节码(bytes)转换为数字
  • 位运算符
  • 原码、反码、补码
  • 运算实例
  • & —— 位与
  • | —— 位或
  • ~ —— 取反
  • >>、<< —— 右移位、左移位
  • 最终代码


 
 
 
 
 
 
 
 

什么是base64

base64是对数据进行编码的方式之一,是最基础的8bit字节码的编码方式。

 

基本原理(以ASCII为例):

1、将数据按每3个字节断开( 3 * 8bit = 24,后面会将24以6bit的形式重组,再将6bit填0补充为8bit)

2、获取要传输的数据中每个字节的ASCII编码

3、将3个字节的ASCII编码转换为二进制,一共24bit

4、将24bit每6个拆分为一组,组成4组

5、再对4组6bit数,在左侧补0,补充为8bit数。此步可省略

6、以新生成的二进制数所对应的十进制数为索引

7、通过索引值和Base64编码表,将每个字节的数据替换为编码后的数据

8、对数据最后不足3个字节的数据,补充 0x00(\x00)到3个字节

9、对最后替换后的编码数据,补了几个 \x00 ,就替换最后几个数据为 ==

 

末尾的处理:末尾可能出现不足3个字节的情况,不足则补0 补至3个字节,补0的字节要用 “=” 表示

 
 
 
 

进制转换关系

十进制转其他进制都是除对应进制的数字,除到0后将所有余数从下向上拼接成一个从左到右的数字,此数字即为对应进制中所代表的数字

其他进制转十进制都是按从右到左的位置,对应位置上的数乘进制数的从0~n次方(取决于有几位数),再将乘出来的结果相加,即为十进制中对应的结果
 

二进制 —— 0、1组合构成,8bit即为一个字符由8个0、1为一组表示。如 10010110 代表

  • 1 —— 1 * 27 = 128
  • 0 —— 0 * 26 = 0
  • 0 —— 0 * 25 = 0
  • 1 —— 1 * 24 = 16
  • 0 —— 0 * 23 = 0
  • 1 —— 1 * 22 = 4
  • 1 —— 1 * 21 = 2
  • 0 —— 0 * 20 = 0

转换为10进制则为 128 + 0 + 0 + 16 + 0 + 4 + 2 + 0 = 150

 

八进制 —— 以 0o 开头,不可省略,后方数字以满8进一进行计算。8进制数字从右到左分别代表 80、81、82、83…,如:0o24 则代表

  • 0o - 八进制标识
  • 1 - 2 * 81 = 16
  • 4 - 4 * 80 = 4

转换为10进制则为 16 + 4 = 20

 

十六进制 —— 以 0x 开头,不可省略,后方数字以满16进一进行计算。16进制数字从右到左分别代表 160、161、162…,如:0x61则代表

  • 0x —— 十六进制标识
  • 6 —— 6 * 161 = 96
  • 1 —— 1 * 160 = 1

转换为10进制则为 96 +1 = 97

 
 

十进制转二进制

十进制数 97 转二进制

  • 97 / 2 —— 48 ··· 1
  • 48 / 2 —— 24 ··· 0
  • 24 / 2 —— 12 ··· 0
  • 12 / 2 —— 06 ··· 0
  • 06 / 2 —— 03 ··· 0
  • 03 / 2 —— 01 ··· 1
  • 01 / 2 —— 00 ··· 1

自下向上组合数字,不满8位则在左侧以0填充 —— 01100001

python中可以直接使用 bin() 方法将十进制转换为二进制

python中十六进制的补码 python 16进制运算_python中十六进制的补码

 
 

十进制转八进制

十进制数 97 转八进制

  • 97 / 8 —— 12 ··· 1
  • 12 / 8 —— 01 ··· 4
  • 01 / 8 —— 00 ··· 1

转换为八进制后结果为,从下向上拼接一个自左向右的数字 —— 0o141

python中可以直接使用 oct() 方法将十进制转换为十六进制

python中十六进制的补码 python 16进制运算_十六进制_02

 
 

十进制转十六进制

十进制数 97 转十六进制

  • 97 / 16 —— 06 ··· 1
  • 06 / 16 —— 00 ··· 6

转换为十六进制结果为 —— 0x61

python中可以直接使用 hex() 方法将十进制转换为十六进制

python中十六进制的补码 python 16进制运算_数据_03

 
 

其他进制转10进制

借助 int(对应进制字符, 进制数) 转换为10进制

如:

16进制转10进制

python中十六进制的补码 python 16进制运算_数据_04

 
 

十六进制转二进制

十六进制

二进制

十六进制

二进制

十六进制

二进制

十六进制

二进制

0

0000

1

0001

2

0010

3

0011

4

0100

5

0101

6

0110

7

0111

8

1000

9

1001

a

1010

b

1011

c

1100

d

1101

e

1110

f

1111

 
 
 
 

关于数据在内存中的大小端模式

大小端模式是两种数据在内存中存储的不同模式。

 

数据的高低位,如一个十六进制数据 0x12345678,自左向右为从 高位低位

  • 0x12 —— 高位
  • 0x34 —— ↓
  • 0x56 —— ↓
  • 0x78 —— 低位

 

内存的高低地址,如下

  • 0x1000 —— 低地址
  • 0x1001 —— ↓
  • 0x1002 —— ↓
  • 0x1003 —— 高地址

 

大小端模式:

内存地址

大端模式

小端模式

0x1000

0x12

0x78

0x1001

0x34

0x56

0x1002

0x56

0x34

0x1003

0x78

0x12

大端模式将数据的高位存放到内存的低地址小端模式将数据的高位存放到内存的高地址

 

大端模式是按照从左到右的顺序存储数据,更符合人的阅读和数据的网络传输(TCP协议规定)

 
 

Python将字节码(bytes)转换为数字

调用方法: int.from_bytes(bytesData, ‘big’ / ‘little’)

 

将字节码转换后的数字转换为十六进制的方法:hex()

 

python3中将字符转为字节码的方式 —> ‘abc’.encode()

如:

python中十六进制的补码 python 16进制运算_数据_05

 

小端模式如下:

python中十六进制的补码 python 16进制运算_python中十六进制的补码_06

 
 
 
 

位运算符

运算符

含义

&

位与

l

位或

~

取反

^

亦或

<<

左移位

>>

右移位

python解释器会自动将乘法、除法等运算自动的转换为移位运算,所以在python中移位运算符相比于其他运算符并没有明显的效率优势

 
 

原码、反码、补码

三码的出现是为了解决计算机中的减法运算(计算机的运算器只有加法),通过补码的方式来进行减法运算

 

原码:直接将其他进制转化为二进制的形式

 

反码:

  • 正数 —— 按原码按位取反
  • 负数 —— 符号位不变,其余位置按位取反

 

补码:

  • 正数 —— 其原码本身
  • 负数 —— 符号位不变,其余位置按位取反后 + 1。另外一个求补码的方法,符号位不变,负数中由低位向高位找到第一个1,保留该为的1及更低位的所有0,对其余位置求反。

 

如 -2,法一 :

  • 原码 —— 1000 0010
  • 反码 —— 1111 1101
  • 补码 —— 1111 1110

法二:

  • 原码 —— 1000 0010
  • 补码 —— 1111 1110

 

实例1、 6 - 2 —> 6 + ( -2 ):

原码:

  • 6 —— 0000 0110
  • -2 —— 1000 0010

补码:

  • 6 —— 0000 0110
  • -2 —— 1111 1110

运算:

python中十六进制的补码 python 16进制运算_十六进制_07

 
 

运算实例

运算时对应位置上数字根据运算符取与、或等,得出结果再转化为10进制

 
 

& —— 位与

例: 6 & 4 —> 4

python中十六进制的补码 python 16进制运算_python_08


解析:

  • 6 —— 0b0110
  • 4 —— 0b0100

6 | 4 —> 0b0110 | 0b0100

  • 0b0110
  • 0b0100
  • 0b0100 —— 4

所以结果为4

 
 

| —— 位或

例: 1 | 2 —> 3

python中十六进制的补码 python 16进制运算_进制_09


解析:

  • 1 —— 0b0001
  • 2 —— 0b0010

1 | 2 —> 0b0001 | 0b0010

  • 0b0001
  • 0b0010
  • 0b0011 —— 3

所以结果为3

 
 

~ —— 取反

例: ~12

python中十六进制的补码 python 16进制运算_进制_10

解析:

  • 12 —— 0000 0110

python中十六进制的补码 python 16进制运算_十六进制_11

 
 

>>、<< —— 右移位、左移位

如其名,向左右移动二进制数所在的位置

下例,均为python3.7中的移位运算结果,不全具有通用性

 

正数 5:

  • 5 —— 0000 0101
  • 5 >> 1 —— 0000 0010 —— 2
  • 5 << 1 —— 0000 1010 —— 10

 

负数 -5:

  • -5 —— 1000 0101
  • -5 >> 1 —— 1000 0011 —— -3
  • -5 << 1 —— 1000 1010 —— -10

 
 
 
 
 
 

最终代码

alphabet = b'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'


def myBase64(src):

    # 最终返回的二进制字节码序列
    res = bytearray()

    # 获取输入的字符的字节长度,判断要从哪断开
    length = len(src)

    # 初始化一个r,用于记录输入的src最后需要补几个0
    # 才能补齐为三个字节
    r = 0

    # 进行字节分组,分为每三个字节一组
    for offset in range(0, length, 3):
        # 将所有可以组成3个字节的数据分组,避免分组中出现空
        if offset + 3 <= length:
            triple = src[offset:offset + 3]
        else:
            # 将剩下的不足3个字节的数据单独分组
            triple = src[offset:]

            # 记录需要给最后的字节补几个0x00
            r = 3 - len(triple)

            # 给最后的分组补充0x00
            triple = triple + '\x00' * r

        # 1、通过triple.encode()将字符转为字节(bytes)
        # 2、通过大端模式(视系统而定, 为了保证数据的顺序不会反过来),将数据从内存中读出
        # 3、将bytes数据转换为十进制的数值 int.from_bytes()
        b = int.from_bytes(triple.encode(), 'big')

        # 通过对二进制的数据进行移位操作,来将 3个字节 共24个bit 重组为 4个 6bit的数据
        #    由于使用的是大端读取的数据,所以数据的顺序是从左至右
        #    此处将数据 b(代表3个一组的字符数据的字节码的10进制数) 进行移位(操作其二进制数据),以重组数据bit
        for i in range(18, -1, -6):
            # 第一次移动,只保留最左侧的6位
            if i == 18:
                # index为移位重组为6bit之后,其二进制数据所对应的整数,此数即为Base64编码表中 字符的索引值
                index = b >> i
            else:
                # 当后面移动时,每次移动6n位,再通过 & 为与运算符,保留最后的6位,其余均为补位0或 & 运算后的0
                index = b >> i & 0x3F # 0x3F 为 0b0011 1111,用于仅保留移位后最右侧的6位二进制数

            # alphabet为Base64对应表
            res.append(alphabet[index])

        # 此处通过上面记录的补了几个0x00,将其替换为 "=" 进行标识
        for i in range(1, r+1):
            # 0x3D为 = 的ascii码
            res[-i] = 0x3D

    return res
testStr = 'abcd'
print(myBase64(testStr))

使用python的base64包

import base64
testStr = 'abcd'
print(base64.b64encode(testStr.encode()))