Effective Python系列(1.1):区别bytes和str

部署运行你感兴趣的模型镜像

本篇文章是 Effective Python 这本书的第一章,本章的主要内容是什么样的代码风格才是比较符合 Python 语言。

Python 当中,bytesstr 是两种不同的数据结构。使用时,需要注意两者区别:

  1. bytes 包含的是由 8 位值所组成的序列,而 str 包含的是由 Unicode 字符组成的序列,可以指定解码格式,如 utf-8

  2. bytesstr 之间的转换,可以使用 encode()decode() 方法。

  3. bytesstr 这两种实例不能在某些操作符(例如>、==、+、%操作符)上混用

  4. 从文件读取或者写入二进制数据时,应该以 'rb'(或者'wb')这样的二进制模式打开文件

  5. 如果要从文件中读取/写入的是 Unicode 数据,则必须注意系统采用的默认编码方案。可以使用下面的代码判断:

import sys
sys.getdefaultencoding()

下面写了两个程序:

  1. 测试 bytesstr 之间的相互转换。
  2. 命令行程序:测试文件的创建、读取、写入和删除。该程序最大的亮点是提供了简易的自动补全功能。
import sys
import json
from pathlib import Path
from prompt_toolkit import prompt
from prompt_toolkit.completion import Completer, Completion


def to_str(data, encoding="utf-8"):
    """
    将字节数据转换为字符串。

    参数:
    data: 字节数据
    encoding: 字符串的编码格式,默认为utf-8

    返回:
    转换后的字符串
    """
    if isinstance(data, bytes):
        return data.decode(encoding)
    elif isinstance(data, str):
        return data
    else:
        raise TypeError(f"Expected bytes, got {type(data)}")

def to_bytes(data, encoding="utf-8"):
    """
    将字符串转换为字节数据。

    参数:
    data: 字符串数据
    encoding: 字节编码格式,默认为utf-8

    返回:
    转换后的字节数据
    """
    if isinstance(data, str):
        return data.encode(encoding)
    elif isinstance(data, bytes):
        return data
    else:
        raise TypeError(f"Expected str, got {type(data)}")

class JsonFileManager:
    def __init__(self, file_path):
        """
        初始化文件管理器。

        参数:
        file_path: 文件路径
        """
        self.file_path = Path(file_path).resolve()  # 转换为绝对路径

    def create(self):
        """
        创建一个空的JSON文件。
        """
        try:
            if not self.file_path.exists():
                with open(self.file_path, 'w') as f:
                    json.dump({}, f, indent=4)
                print(f"文件 {self.file_path.name} 已创建。")
            else:
                print(f"文件 {self.file_path.name} 已存在。")
        except (IOError, PermissionError) as e:
            print(f"创建文件失败: {e}")

    def read(self):
        """
        读取并显示JSON文件的内容。
        """
        try:
            if self.file_path.exists():
                with open(self.file_path, 'r') as f:
                    data = json.load(f)
                    print("读取的文件内容:", data)
            else:
                print(f"文件 {self.file_path.name} 不存在。")
        except (IOError, json.JSONDecodeError) as e:
            print(f"读取文件失败: {e}")

    def delete(self):
        """
        删除文件。
        """
        try:
            if self.file_path.exists():
                self.file_path.unlink()
                print(f"文件 {self.file_path.name} 已删除。")
            else:
                print(f"文件 {self.file_path.name} 不存在。")
        except (IOError, PermissionError) as e:
            print(f"删除文件失败: {e}")

    def update(self, data):
        """
        更新文件内容。
        """
        try:
            with open(self.file_path, 'w') as f:
                json.dump(data, f, indent=4)
            print(f"文件 {self.file_path.name} 已更新。")
        except (IOError, PermissionError) as e:
            print(f"更新文件失败: {e}")

    def rename(self, new_name):
        """
        重命名文件。
        """
        try:
            new_path = self.file_path.parent / new_name
            self.file_path.rename(new_path)
            self.file_path = new_path
            print(f"文件已重命名为 {new_name}.")
        except (FileNotFoundError, PermissionError) as e:
            print(f"重命名文件失败: {e}")

    def move(self, new_path):
        """
        移动文件到新位置。
        """
        try:
            new_path = Path(new_path).resolve()
            if not new_path.parent.exists():
                print(f"目标路径 {new_path.parent} 不存在.")
                return
            self.file_path.rename(new_path)
            self.file_path = new_path
            print(f"文件已移动到 {new_path}.")
        except (FileNotFoundError, PermissionError) as e:
            print(f"移动文件失败: {e}")

# 定义命令补全的列表
COMMANDS = ["create", "read", "delete", "update", "rename", "move", "quit"]

class CommandCompleter(Completer):
    def __init__(self):
        self.commands = COMMANDS  # 定义可补全的命令列表

    def get_completions(self, document, complete_event):
        """
        返回与输入的部分文本匹配的命令。
        """
        text = document.text.strip()
        for command in self.commands:
            if command.startswith(text):
                yield Completion(command, start_position=-len(text))


def setup_readline():
    """
    配置自动补全功能。
    """
    completer = CommandCompleter()

    
    # 使用prompt_toolkit的prompt函数获取输入,并启用自动补全
    user_input = prompt('请输入命令(create, read, delete, update, rename, move, quit): ', completer=completer)
    
    return user_input    


def main():
    """
    主函数,处理文件操作与字节数据转换。
    """
    # setup_readline()
    
    file_path = input("请输入文件路径: ").strip()
    # file_path = 'dataset.json'
    manager = JsonFileManager(file_path)

    while True:
        command = setup_readline().strip().lower()
        if command == "quit":
            break
        elif command == "update":
            new_data = input("请输入新的JSON数据: ")
            try:
                # 对用户输入的JSON数据进行验证
                new_data = json.loads(new_data)
                manager.update(new_data)
            except json.JSONDecodeError:
                print("输入的JSON数据格式错误")
                continue
        elif command in ["create", "read", "delete", "rename", "move"]:
            if command == "rename":
                new_name = input("请输入新的文件名: ").strip()
                manager.rename(new_name)
            elif command == "move":
                new_location = input("请输入新的文件路径: ").strip()
                manager.move(new_location)
            else:
                getattr(manager, command)()
        else:
            print("无效命令,请输入 'create', 'read', 'delete', 'update', 'rename', 'move' 或 'quit'")


def main2():
    # 示例:创建一个包含字节数据的文件,并读取它
    file_path = "test_bytes.json"

    # 创建 JsonFileManager 实例
    manager = JsonFileManager(file_path)

    # 1. 创建文件并写入字节数据
    manager.create()  # 文件已创建,但内容为空 {}

    # 假设用户想将字符串 "Hello, World!" 转换为字节并写入文件
    data_to_write = to_bytes("Hello, World!")  # 使用 to_bytes 转换为字节流

    # 更新文件内容,将字节数据以 JSON 格式存储
    manager.update({"message": data_to_write.hex()})  # 将字节数据转为十六进制字符串存储

    # 2. 读取文件并将字节数据还原为字符串
    manager.read()  # 输出 { "message": "48656c6c6f2c20576f726c6421" }

    # 获取 JSON 数据
    with open(file_path, 'r') as f:
        data = json.load(f)
        # 从十六进制字符串转换回字节流
        message_bytes = bytes.fromhex(data["message"])

    # 使用 to_str 函数将字节数据转换为字符串
    message_str = to_str(message_bytes)

    print(f"读取的消息:{message_str}")  # 输出 "Hello, World!"

if __name__ == "__main__":
    main()

程序使用例子:
在这里插入图片描述

您可能感兴趣的与本文相关的镜像

Python3.10

Python3.10

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

<think>好的,我现在需要帮用户用Python实现Base64解码的原理。首先,我得回忆一下Base64编码的基本原理,这样才能逆向解码过程。 Base64编码是将二进制数据转换成由64个字符组成的ASCII字符串。这些字符包括A-Z、a-z、0-9,以及+/,有时候末尾可能会有=作为填充。解码的话,自然是要把这个过程反过来,把Base64字符串转回原始的字节数据。 首先,用户可能希望了解Base64解码的步骤,然后逐步用Python实现。我需要分步骤来拆解这个问题,确保每一步都正确。 第一步,Base64的字符表。解码需要将每个字符转换回对应的6位值。所以,我需要先创建一个字符到索引的映射字典。例如,字符'A'对应0,'B'对应1,一直到'/'对应63。这一步应该不难,可以用Python的字典推导式来实现。 接下来,输入的Base64字符串可能需要处理填充符=。通常,Base64字符串的长度是4的倍数,如果有等号的话,会在末尾出现一个或两个。解码时需要先去掉这些等号,记录他们的数量,因为在处理的时候可能需要考虑最后几个字节的问题。比如,如果有两个等号,说明原始数据最后只剩下一个字节,四个Base64字符中的最后两个是填充的。 然后,将每个字符替换成对应的6位二进制值,然后将这些二进制拼接起来。比如,四个Base64字符对应的4*6=24位,分成三个8位的字节。这时候需要考虑,如果原始数据长度不是3的倍数,编码时会有填充,解码时就需要处理这些情况,去掉相应的填充字节。 现在,具体到Python实现的话,步骤如下: 1. 创建Base64的字符到索引的映射表。注意,标准的Base64字符顺序是A-Z,a-z,0-9,+,/。所以,我可以生成一个字符串,按顺序排列这些字符,然后用enumerate生成字典,比如char_to_index = {char: index for index, char in enumerate(base64_chars)},其中base64_chars是正确顺序的字符串。 2. 处理输入字符串中的填充符。首先去除末尾的=,并记录它们的数量。例如,输入字符串可能是'Zg==', 去除后得到'Zg',并记录有两个等号,说明原始数据少两个字节,最后需要处理。 3. 将每个字符转换为对应的6位数值。例如,对于'Zg',Z对应的索引是25,g是32。然后,将这些数值转换成6位的二进制字符串,拼接起来。需要注意的是,每个数值要转换为6位的二进制,前面补零,比如25的二进制是011001,而不是11001。 4. 将拼接后的二进制字符串按8位一组分割,得到字节数据。比如,总共有24位的话,分成3个8位的字节。如果原始数据有填充,可能需要截断后面的部分。例如,如果有两个等号,总共有24- (2*6)=12位?或者需要重新计算。这里可能需要更仔细的思考。 比如,假设原数据在编码时,最后不足3字节,补了1或2个等号。解码时,根据等号的数量,确定原始数据的字节数。例如,每个等号代表原数据缺少的字节数。例如,两个等号说明原数据缺少2字节,即原数据长度是1字节,编码时补了两个等号。此时,在解码后的字节中,应该只保留第一个字节,后面的可能要去掉。 或者,更准确地说,原数据在编码时,每3字节一组,如果余1字节,则补两个等号;余2字节,补一个等号。所以解码时,如果有两个等号,说明原始数据在编码时是1字节,转换成4个Base64字符中的前两个有效,后面两个是填充,所以总共有2*6=12位,转换为1个字节(8位)?或者可能更复杂。 这部分可能需要更详细的分析。例如,假设输入字符串是四个字符,但后两个是填充的=,则实际有效的只有前两个字符,对应的12位二进制,应该分成1个字节(8位),剩下的4位可能被忽略?或者如何处理? 例如,原数据是1字节(8位),编码时将其分为两个6位组(需要补4位0),得到两个Base64字符,然后补充两个=。解码时,将这两个字符的12位二进制中,取前8位作为原始字节,剩下的4位是填充的0,可以忽略。因此,如果等号的数量是n_pad,那么原始字节数为 (4 - n_pad) * 6 * 4 / 8?或者应该用另一种方式计算? 这里可能需要一个公式:原始字节数 = (length_of_base64_str_without_pad * 6) // 8 - (n_pad if n_pad > 0 else 0) 或者更简单来说,如果输入的Base64字符串(去掉等号)的长度是n,则总共有n*6位。将其分成8位一组,得到的总字节数是 (n*6) //8。例如,当n=4时,4*6=24位,分成3字节。当n=2时,12位,分成1字节余4位。但因为原数据可能被填充,这时候n_pad等于2时,说明原数据是1字节,所以应该只取前8位,即得到1字节。 因此,处理的时候,在拼接所有二进制位后,如果存在n_pad,则需要计算有效位数。例如,当n_pad=2时,有效位数为 (4 - 2) *6 - (填充导致的0位?或者可能需要调整。这部分可能需要更仔细的分析。 例如,假设原数据是1字节(8位),编码过程是将其拆分为两个6位的块:前6位后2位(补0到6位),得到两个Base64字符。然后在编码结果后面补两个=。解码时,这两个字符对应的二进制是前6位后两位,加上补的四个0?所以总共有6+6=12位,其中前8位是原数据,剩下的4位是被填充的0,应该被丢弃。 因此,在处理时,如果有n_pad,总的有效位数是 (4 - n_pad)*6*每个字符的位数减去填充带来的补零。这可能比较复杂。或者,更简单的方式是先拼接所有字符对应的6位,得到一个二进制字符串,然后根据是否有填充符来截断末尾的某些位。 例如,如果有两个填充符,那么说明原数据只有1字节,即8位。而每个Base64字符对应6位,所以有效字符数是2,总共有2*6=12位二进制。而我们需要取前8位,剩下的四位是填充的0,应该丢弃。这时候,总字节数是1。 同理,如果有一个填充符,说明原数据是2字节(16位),编码后的有效字符数是3,总共有3*6=18位,而原数据是16位,所以需要截断最后两位的0。 所以,在Python代码中,处理填充的步骤可能是: - 计算n_pad(等号的数量) - 有效字符数 = len(base64_str) - n_pad - 总位数 = effective_chars *6 - 如果n_pad == 1: 总位数 -= 4 # 因为编码时添加了4位0(比如,原数据是2字节,即16位,16+4=20,分成三个6位组得到18位,所以总共有3*6=18位,其中最后两位是补的0) - elif n_pad ==2: 总位数 -= 8 # 原数据是1字节,8位,补了4个0,变成12位,即两个6位组,所以总位数是12,但原数据只有8位有效,所以总位数要减去8?或者可能我的理解有误。 这部分可能需要更详细的数学分析。或者,可以先将所有有效字符转换为二进制字符串,然后根据填充情况调整截断的位置。 或者,另一个思路是,不管填充,将有效字符转换为二进制后,计算总共有多少位,然后取尽可能多的8位组,忽略剩下的位数。这可能会有问题,但或许在大多数情况下可行。不过这可能不准确,因为当存在填充时,末尾的0是需要被去掉的。 或者,正确的做法应该是: 当有n_pad个填充符时: - 如果n_pad ==1,说明原数据在编码时是2字节,所以总共有3个6位块,总共有3*6=18位,原数据是2*8=16位,所以最后两位是填充的0,应该被去掉。所以,二进制字符串的总长度应为16位,即取前16位。 - 如果n_pad ==2,说明原数据是1字节,总共有2个6位块,共12位,原数据是8位,所以取前8位,剩下的4位是填充的0,应被去掉。 因此,总的处理步骤是: 1. 去掉输入字符串中的填充符,得到有效字符串,并记录n_pad。 2. 将每个有效字符转换为对应的6位二进制字符串,拼接成一个完整的二进制字符串。 3. 根据n_pad的值,截断二进制字符串的末尾的填充位: - 如果n_pad ==1,总有效位数为 (len(effective_str)*6) -4 → 因为原数据是2字节,编码时添加了4位0,所以需要减去4位。 - 如果n_pad ==2,总有效位数为 (len(effective_str)*6) -8 → 原数据是1字节,编码时添加了8位0? 这里可能需要重新计算。 例如,原数据是1字节(8位),编码时将其拆分为两个6位块:前6位后2位,后2位后面补4个0,形成第二个6位块。所以编码后的两个块对应的二进制是: 原数据: 8位 → 拆分为 6位 + 2位 → 第二个块补4个0,得到6位。所以总共有6+6=12位,其中前8位有效,后面4位是填充的0。因此,解码时需要截断后4位,得到前8位。即总位数是8。 所以,当n_pad=2时,总位数应该是有效字符数*6 -4* (n_pad) → 例如,有效字符数是2 → 2*6=12 → 12-8=4? 不对,这样会导致位数不够。可能我的逻辑有问题。 或者,原数据长度是1字节(8位),编码时将其转换为两个6位的块,所以总共有12位,但其中前8位是原数据,后4位是填充的0。解码时需要取前8位。所以总的有效位数是8位。 在这种情况下,有效字符数是2,所以总二进制位数是12。但是有效位数是8,所以需要截断最后4位。即,当n_pad=2时,总有效位数是有效字符数*6 - 4*(2) → 2*6=12 -8=4? 这显然不对,因为8位是原数据。 这可能说明我的计算方式有问题。或许应该这样计算: 当n_pad=1时,原数据是2字节(16位),编码后有效字符数是3,即3*6=18位。原数据是16位,所以需要截断最后两位(因为18-16=2,即填充了两位的0)。 当n_pad=2时,原数据是1字节(8位),有效字符数是2,即2*6=12位,原数据是8位,所以需要截断最后4位(12-8=4)。 所以,截断后的二进制长度为: 总有效位 = (有效字符数 *6) - (n_pad * (6 - (原数据位数 %3 ? ))) → 或者更直接: 当n_pad ==1时,截断2位; 当n_pad ==2时,截断4位。 因此,在Python中,处理的时候: binary_str = ''.join([format(index, '06b') for index in indices]) # 拼接所有6位二进制 if n_pad ==1: binary_str = binary_str[:-2] elif n_pad ==2: binary_str = binary_str[:-4] 这样就能得到正确的二进制字符串,然后将其按8位分割。 例如,对于n_pad=2的情况,假设有效字符是2个,拼接后的二进制是12位,减去4位,得到8位,分割为一个字节。 对于n_pad=1的情况,有效字符3个,拼接后的二进制是18位,减去2位,得到16位,分割为两个字节。 这样就可以正确还原原始数据。 现在,把这些步骤转化为Python代码: 首先,处理输入字符串: base64_str = input_str.rstrip('=') n_pad = len(input_str) - len(base64_str) 然后,将每个字符转换为对应的索引: indices = [char_to_index[c] for c in base64_str] 接着,转换为二进制字符串: binary_str = ''.join(format(index, '06b') for index in indices) 处理填充导致的截断: if n_pad ==1: binary_str = binary_str[:-2] elif n_pad ==2: binary_str = binary_str[:-4] 然后,将binary_str按8位分割: bytes_list = [] for i in range(0, len(binary_str), 8): byte_bin = binary_str[i:i+8] if len(byte_bin) <8: # 如果最后不足8位,可能发生在没有填充的情况下,但根据上面的处理应该不会有这种情况? break bytes_list.append(int(byte_bin, 2)) 最后,将bytes_list转换为bytes对象。 现在,将这些步骤组合起来,注意处理可能的错误情况,比如输入字符串包含非法字符,或者长度不正确(如长度不是4的倍数,或者填充符的位置不正确)。 但用户可能只需要一个基本的实现,所以可能暂时忽略错误处理,假设输入是有效的Base64字符串。 现在,编写Python代码的大致流程: 1. 定义base64字符集: base64_chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' char_to_index = {c:i for i,c in enumerate(base64_chars)} 2. 处理输入字符串: input_str = '...' # 用户提供的输入 # 去除末尾的=,并计算填充数 stripped_str = input_str.rstrip('=') n_pad = len(input_str) - len(stripped_str) # 检查输入长度是否合法,有效长度应为4的倍数,或者加上填充符后的总长度是4的倍数? # 如果输入的长度不是4的倍数,可能无效。但用户可能希望处理这种情况,或者假设输入正确。 # 暂时忽略错误处理。 3. 转换每个字符为索引: try: indices = [char_to_index[c] for c in stripped_str] except KeyError as e: # 处理非法字符 raise ValueError("Invalid Base64 character: {}".format(e)) 4. 转换为二进制字符串: binary_str = ''.join(format(index, '06b') for index in indices) 5. 处理填充导致的截断: if n_pad ==1: binary_str = binary_str[:-2] elif n_pad ==2: binary_str = binary_str[:-4] 6. 分割为8位,转换为字节: bytes_list = [] for i in range(0, len(binary_str), 8): byte_str = binary_str[i:i+8] if len(byte_str) !=8: break # 忽略最后不足8位的部分,但根据前面的处理,这种情况应该不会发生? bytes_list.append(int(byte_str, 2)) result = bytes(bytes_list) 现在,测试这个代码是否正确。 例如,测试字符串"Zg==",解码后应为b'f'。 分解步骤: input_str = 'Zg==' stripped_str = 'Zg',n_pad=2. 转换字符: char_to_index['Z'] = 25 → binary 011001 char_to_index['g'] = 32 → binary 100000 binary_str = '011001100000' → 总长度12位。 n_pad=2,截断4位 → 12-4=8 → binary_str = '01100110' → 分割为8位,得到0b01100110=102 → ASCII码是'f'。正确。 另一个测试用例,比如"TWFu",解码为b'Man'。 处理步骤: input_str = 'TWFu' → stripped_str = 'TWFu', n_pad=0. 各个字符的索引: 'T' → 19 → 010011 'W' → 22 → 010110 'F' → 5 → 000101 'u' → 46 → 101110 拼接后的二进制:010011 010110 000101 101110 → 合并成24位: 01001101 01100001 01101110 → 对应十进制的77, 97, 110 → ASCII为 M, a, n → b'Man'。正确。 另一个例子,输入"QQ==",解码为b'A'。 处理: input_str = 'QQ==' stripped_str = 'QQ', n_pad=2. 字符Q对应的索引:16 → 010000 另一个Q也是16 → 010000. binary_str = 010000 010000 → 合并为010000010000 → 总长度12位。截断4位,得到前8位:01000001 → 65 → 'A'。正确。 那代码似乎可以处理这些情况。 但需要注意,当输入的Base64字符串的长度(不包括填充符)不是4的倍数时,可能需要处理错误。比如,如果用户输入的是3个字符,而n_pad为0,这可能不合法。但根据Base64规范,原始编码后的字符串长度必须是4的倍数,填充符的数量可能为0、1或2。所以,正确的Base64字符串的长度应该为4的倍数。所以在代码中应该检查输入的长度是否合法,但用户可能希望忽略这些错误处理,或者自行处理。 综上,完整的Python函数大致如下: def base64_decode(input_str: str) -> bytes: base64_chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' char_to_index = {c:i for i, c in enumerate(base64_chars)} stripped_str = input_str.rstrip('=') n_pad = len(input_str) - len(stripped_str) # 检查输入长度是否合法 if (len(input_str) %4 !=0): raise ValueError("Invalid Base64 input length") # 检查填充符是否在正确的位置 if n_pad >0 and (len(stripped_str) %4 not in [2,3]): # 例如,当n_pad=1时,stripped_str的长度应为3 → 3+1=4 → 允许 # 当n_pad=2时,stripped_str长度应为2 → 2+2=4 # 否则,填充符的位置不正确 raise ValueError("Invalid padding") try: indices = [char_to_index[c] for c in stripped_str] except KeyError as e: raise ValueError(f"Invalid character found: {e}") binary_str = ''.join(f"{index:06b}" for index in indices) # 处理填充导致的截断 if n_pad ==1: binary_str = binary_str[:-2] elif n_pad ==2: binary_str = binary_str[:-4] # 分割为8位并转换为字节 bytes_list = [] for i in range(0, len(binary_str), 8): byte_str = binary_str[i:i+8] if len(byte_str) <8: break # 忽略不足8位的部分 bytes_list.append(int(byte_str, 2)) return bytes(bytes_list) 但用户可能不需要错误处理,所以可以简化代码,不检查输入是否合法。 现在,用户的问题是需要帮助实现Base64解码的原理,所以需要将这个过程分步骤解释,并用代码展示。 可能的步骤解释: 1. 创建Base64字符到6位索引的映射表。 2. 去除输入字符串的填充符=,并记录填充符的数量。 3. 将每个字符转换为对应的6位二进制,拼接成二进制字符串。 4. 根据填充符的数量,截断二进制字符串末尾的填充位。 5. 将处理后的二进制字符串按8位分割,转换为字节数据。 这样,用户就能理解Base64解码的步骤,并用Python代码实现。 可能还需要解释为什么需要截断这些位数,这涉及到Base64编码时的填充规则。例如,当原始数据不是3的倍数时,编码时会添加填充符,解码时则需要根据填充符数量去除相应的补零。 总结,最终的Python代码应该按照上述步骤实现,并且通过示例测试验证正确性。</think>下面分步讲解如何用Python实现Base64解码原理,并提供代码实现。 --- ### **Base64解码原理** Base64将每3字节(24位)的二进制数据编码为4个字符。解码时需逆向操作: 1. 去除填充符`=`,计算填充数量 2. 将字符还原为6位二进制 3. 拼接所有二进制后按8位分割 4. 根据填充数量截断多余的补零位 --- ### **Python实现步骤** #### 1. 创建Base64字符映射表 ```python base64_chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' char_to_index = {char: idx for idx, char in enumerate(base64_chars)} ``` #### 2. 处理输入并计算填充 ```python input_str = "UGFkZGVkU3RyaW5n" # 示例输入 stripped = input_str.rstrip('=') n_pad = len(input_str) - len(stripped) ``` #### 3. 字符转6位二进制并拼接 ```python binary_str = ''.join([format(char_to_index[c], '06b') for c in stripped]) ``` #### 4. 根据填充截断多余位 ```python if n_pad == 1: binary_str = binary_str[:-2] # 截2位(原数据为2字节时补的0) elif n_pad == 2: binary_str = binary_str[:-4] # 截4位(原数据为1字节时补的0) ``` #### 5. 按8位分割并转为字节 ```python bytes_list = [] for i in range(0, len(binary_str), 8): byte = binary_str[i:i+8] if len(byte) == 8: bytes_list.append(int(byte, 2)) result = bytes(bytes_list) ``` --- ### **完整代码实现** ```python def base64_decode(input_str: str) -> bytes: base64_chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' char_to_index = {char: idx for idx, char in enumerate(base64_chars)} stripped = input_str.rstrip('=') n_pad = len(input_str) - len(stripped) # 将字符转换为6位二进制字符串 binary_str = ''.join(format(char_to_index[c], '06b') for c in stripped) # 根据填充截断多余位 if n_pad == 1: binary_str = binary_str[:-2] elif n_pad == 2: binary_str = binary_str[:-4] # 按8位分割并转为字节 bytes_list = [] for i in range(0, len(binary_str), 8): byte_str = binary_str[i:i+8] if len(byte_str) == 8: bytes_list.append(int(byte_str, 2)) return bytes(bytes_list) # 测试 encoded_str = "SGVsbG8gV29ybGQh" # "Hello World!"的Base64编码 decoded_bytes = base64_decode(encoded_str) print(decoded_bytes.decode('utf-8')) # 输出: Hello World! ``` --- ### **关键点解释** 1. **填充处理**:末尾的`=`表示原始数据不足3字节,需截断补零位。 2. **二进制拼接**:每个字符对应6位,如`'A'→000000`。 3. **字节分割**:每8位构成一个字节,还原原始二进制数据。 通过以上步骤,即可手动实现Base64解码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值