Base32 的编码和解码问题 | 豆包MarsCode AI 刷题

141 阅读8分钟

问题描述

你需要实现一个 Base32 的编码和解码函数。

相比于 Base32,你可能更熟悉 Base64,Base64 是非常常见的用字符串形式表示二进制数据的方式,在邮件附件、Web 中的图片中都有广泛的应用。

Base32 是 Base64 的变种,与 Base64 不同的地方在于 Base64 以 6 bit 为一组作为索引,而 Base32 以 5 bit 为一组作为索引,每一组用一个 ASCII 字符表示。Base 64 总共需要 64 个字符表示,而 Base32 则只需要 32 个字符表示。

Base32 的编码流程如下:

  • 对二进制数据进行预处理:如果二进制数据的 bit 数目不是 5 的倍数的话,在末尾补 0 直至为 5 的倍数。
  • 以 5 bit 为一组进行分组。
  • 将每一组的 5 bit 二进制转换为索引(0 - 31)。
  • 在索引 - 字符转换表中查询索引对应的字符。
  • 根据原始二进制数据的 bit 数目除以 40 后的余数,确定末尾需要补 + 的数目。
  • 如果原始二进制数据 bit 数目除以 40 后的余数是 0 的话,不需要补 +
  • 如果余数是 8,补 6 个 +
  • 如果余数是 16,补 4 个 +
  • 如果余数是 24,补 3 个 +
  • 如果余数是 32,补 1 个 +

Base32 的索引 - 字符转换表如下:

索引:0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31

字符:9 8 7 6 5 4 3 2 1 0 m n b v c x z a s d f g h j k l p o i u y t

你需要对字符串rawStr进行编码,并对encodedStr进行解码。


解题思路

需要实现一个 Base32 的编码和解码函数。Base32 是一种将二进制数据编码为字符串的方式,类似于 Base64,但使用 5 位二进制数据作为索引,而不是 6 位。

编码流程

  1. 预处理:如果二进制数据的位数不是 5 的倍数,则在末尾补 0 直至为 5 的倍数。
  2. 分组:将二进制数据按 5 位一组进行分组。
  3. 转换:将每一组的 5 位二进制数据转换为索引(0 - 31)。
  4. 映射:在索引 - 字符转换表中查询索引对应的字符。
  5. 补 +:根据原始二进制数据的位数除以 40 后的余数,确定末尾需要补 + 的数目。

解码流程

  1. 划分:按加号划分编码后的字符串。
  2. 转换:将字符转换为二进制码。
  3. 分组:将二进制数据按 8 位一组进行分组。
  4. 转换:将每一组的 8 位二进制数据转换为 ASCII 字符。

索引 - 字符转换表

索引:0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
字符:9 8 7 6 5 4 3 2 1 0 m n b v c x z a s d f g h j k l p o i u y t

解题思路

编码:

-   将输入字符串转换为 ASCII 码,并将其转换为二进制字符串。
-   补零使得二进制字符串的长度是 5 的倍数。
-   将二进制字符串按 5 位一组进行分组,并将每组转换为索引。
-   根据索引 - 字符转换表将索引转换为字符。
-   根据原始二进制数据的位数除以 40 后的余数,确定末尾需要补 `+` 的数目。

解码:

-   按加号划分编码后的字符串。
-   将每个字符转换为索引,并将索引转换为二进制字符串。
-   将二进制字符串按 8 位一组进行分组,并将每组转换为 ASCII 字符。

数据结构选择

  • 索引 - 字符转换表:使用字典来存储索引到字符和字符到索引的映射。
  • 二进制字符串:使用字符串来存储二进制数据。

算法步骤

  1. 编码

    • 将输入字符串转换为 ASCII 码,并将其转换为二进制字符串。
    • 补零使得二进制字符串的长度是 5 的倍数。
    • 将二进制字符串按 5 位一组进行分组,并将每组转换为索引。
    • 根据索引 - 字符转换表将索引转换为字符。
    • 根据原始二进制数据的位数除以 40 后的余数,确定末尾需要补 + 的数目。
  2. 解码

    • 按加号划分编码后的字符串。
    • 将每个字符转换为索引,并将索引转换为二进制字符串。
    • 将二进制字符串按 8 位一组进行分组,并将每组转换为 ASCII 字符。

题解

以下为python版本代码,已通过评测

def solution(rawStr, encodedStr):
    # 索引列表
    indexList = ['9', '8', '7', '6', '5', '4', '3', '2', '1', '0', 'm', 'n', 'b', 'v', 'c', 'x', 'z', 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'p', 'o', 'i', 'u', 'y', 't']
    indexToChar = {i: indexList[i] for i in range(len(indexList))}  # 索引转字符
    charToIndex = {indexList[i]: i for i in range(len(indexList))}  # 字符转索引

    # 编码
    # 1、将字符串转为ascii码再转为二进制(补0)
    asciiStr = [ord(char) for char in rawStr]  # 将字符串转化为字符数组
    binaryStr = ''.join(f'{num:08b}' for num in asciiStr)  # 将单个字符转化为八位二进制数,并赋值给二进制串
    rawBinaryStr = binaryStr
    zeroNumNeeded = (5 - len(binaryStr) % 5) % 5
    # 检查当前二进制串的位数,适当补零
    binaryStr += '0' * zeroNumNeeded
    # 2、按五位一组进行分组,并转化为索引
    rawGroup = [binaryStr[i:i+5] for i in range(0, len(binaryStr), 5)]  # 原始二进制数据分组
    # 转化为索引
    indexRuneArr = [indexToChar[int(val, 2)] for val in rawGroup]
    result1 = ''.join(indexRuneArr)  # 解码结果
    # 补 '+'
    if len(rawBinaryStr) % 40 == 8:
        result1 += '++++++'
    elif len(rawBinaryStr) % 40 == 16:
        result1 += '++++'
    elif len(rawBinaryStr) % 40 == 24:
        result1 += '+++'
    elif len(rawBinaryStr) % 40 == 32:
        result1 += '+'

    # 解码
    # 按加号划分
    decodedStrArr = []
    isPlus = False
    while len(encodedStr) > 0:
        for index in range(len(encodedStr)):
            val = encodedStr[index]
            if val == '+':
                if index == len(encodedStr) - 1:
                    decodedStrArr.append(encodedStr)
                    encodedStr = ''
                    break
                isPlus = True
            else:
                if isPlus:
                    decodedStrArr.append(encodedStr[:index])
                    isPlus = False
                    encodedStr = encodedStr[index:]
                    break
        
    print(decodedStrArr)
    # 1、将字符转为二进制码
    result2 = ''
    for str_ in decodedStrArr:
        binaryDecodeStr = ''
        numOfPlus = 0
        for val in str_:
            if val == '+':
                numOfPlus += 1
            else:
                binaryDecodeStr += f'{charToIndex[val]:05b}'
        startIndex = len(binaryDecodeStr) - len(binaryDecodeStr) % 40  # 最后一组的起始索引
        if numOfPlus == 1:
            binaryDecodeStr = binaryDecodeStr[:startIndex + 32]
        elif numOfPlus == 3:
            binaryDecodeStr = binaryDecodeStr[:startIndex + 24]
        elif numOfPlus == 4:
            binaryDecodeStr = binaryDecodeStr[:startIndex + 16]
        elif numOfPlus == 6:
            binaryDecodeStr = binaryDecodeStr[:startIndex + 8]
        binaryGroup = [chr(int(binaryDecodeStr[j:j+8], 2)) for j in range(0, len(binaryDecodeStr), 8)]  # 二进制分组,每八位一组
        result2 += ''.join(binaryGroup)

    result = result1 + ':' + result2  # 总结果
    print(result)
    return result

if __name__ == "__main__":
    #  You can add more test cases here
    print(solution("foo", "b0zj5+++") == "bljhy+++:bar" )
    print(solution("The encoding process represents 40-bit groups of input bits as output strings of 8 encoded characters.  Proceeding from left to right, a 40-bit input group is formed by concatenating 5 8bit input groups. These 40 bits are then treated as 8 concatenated 5-bit groups, each of which is translated into a single character in the base 32 alphabet.  When a bit stream is encoded via the base 32 encoding, the bit stream must be presumed to be ordered with the most-significant- bit first. That is, the first bit in the stream will be the high- order bit in the first 8bit byte, the eighth bit will be the low- order bit in the first 8bit byte, and so on.", "bljhy+++b0zj5+++") == "maf3m164vlahyl60vlds9i6svuahmiod58l3mi6sbglhmodfcbz61b8vb0fj1162c0jjmi6d58jhb160vlk2mu89b0fj1il9b4ls9oogcak2mu89cvp25pncbuls9oo359i79lncbvjh1ln558ahzknsb4aj1lnscbj7917zc0jh3ln4bafhill9bll3yo09vashbu89cajs9id0buf21n89b5z61b8vb0fj1160vlk2mu89bul3yunz58fj3163vul3pln558a2s166vuj33knfbgj37u60vlds9v0928a3su89v4j29unf58dj5oogc8lsi17fv8sj3l093zk79kd0cals9knsbfz21p64vkz21id4b4p3ml89b4ls9c89bvjhiko8cashiknfbgs79v0vb0fj1162c0jjmi6d4zz3mkn6v9z3yla9cuf3sko158fj316fc0zhiiobb4p3ml89v4j21ol9b5z23pncbuh3m166v8zj5kn6casj5160vkz21p6458a37io459ld5168vak3zkn7bgp7i189muf3moa9b5z35pnf58lj1id4b4hs9pnd58shikoxbash116hv4zs9u61bfz35kndbfz63ba9bgj33oo5v4j3cn89caf3m167v4p79iofc0sh7o09vgpj3u89b0ss9i6sbgljmon4bzz21ol9b0ss9oosbasj5ln558ohsu6158p3zl09vgjj3u8vcvfhcod0blfh3kncczhs9kd0czz3bpnscvp7i17fv8zj1160cbh79u61bfz3bpnscvp79kd0czz3soa9caf3m16dcal3mknv58ohso6b58a3m16fv8ss9p60buf7p16xc0s3mia9b0fj1160vkz21p6458d3siddczz6zkd0czz35ynfbfh79u61bfz3mpn2v8p3z167v4p79uo0vah79kd458p3zl09vajjcn09vul31lns58a3su89v4j79u61bfz3bpnscvp79c67v4p79kdlcassk168vls79iox58jhinz+:foobar" )