在Python中将十进制转换为任意进制数字

4

这个函数接受任何十进制整数,并返回该数字以其指定的32进制形式的字符串表示:

def encodeN(n,N,D="0123456789qwertyuiopasdfghjklzxc"):
    return (encodeN(n//N,N)+D[n%N]).lstrip("0") if n>0 else "0"

例子:

print (encodeN(16002,32))

输出:

ya2

但是我在编写从base-32到base-10的解码函数时遇到了问题。我该如何编写这个函数?我可以输入自定义的非标准字符来扩展基数吗?


2
当你说“回到十进制”时,你是指从一个整数的基于32位字符串表示转换为Python int,对吗?(Python int不是以十进制存储的,所以“十进制”部分有点误导性。) - Mark Dickinson
是的,那就是我想表达的意思 :) - Tomasz Przemski
只使用内置的 base64.b32encode() 有什么问题吗? - zwer
@zwer:我猜它没有使用“0123456789qwertyuiopasdfghjklzxc”。 - Eric Duminil
@zwer 一切都正常,但我想自己编写代码。 - Tomasz Przemski
3个回答

4

您可以作弊:

tmap = str.maketrans('qwertyuiopasdfghjklzxc', 'abcdefghijklmnopqrstuv')
result = int(inputvalue.translate(tmap), 32)

演示:

>>> tmap = str.maketrans('qwertyuiopasdfghjklzxc', 'abcdefghijklmnopqrstuv')
>>> inputvalue = 'ya2'
>>> int(inputvalue.translate(tmap), 32)
16002

int()函数可以将任意进制的数值转换为整数。你只需要使用标准的字母顺序即可。上面的str.translate()调用将你自定义的进制映射到了标准进制。

否则,从左侧开始,取出输入字符串中的每个字符,将其映射为字符映射中的一个整数,并每次乘以基数N:

def decodeN(n, N, D={c: i for i, c in enumerate("0123456789qwertyuiopasdfghjklzxc")}):
    result = 0
    for c in n:
        result = (result * N) + D[c]
    return result

这是一种较慢的方法; str.translate()int() 都使用优化的 C 代码来完成它们的工作,因此将始终比纯 Python 方法更快。
将其转换为递归版本以匹配您的encodeN()实现:
def decodeN(n, N, D={c: i for i, c in enumerate("0123456789qwertyuiopasdfghjklzxc")}):
    return decodeN(n[:-1], N) * N + D[n[-1]] if n else 0

非常感谢!上一个答案最适合我,因为对我来说使用标准的字母顺序有点麻烦。 - Tomasz Przemski

2
使用相同的递归结构,你可以写成这样:
def encodeN(n,N,D="0123456789qwertyuiopasdfghjklzxc"):
    return (encodeN(n//N,N)+D[n%N]).lstrip("0") if n>0 else "0"

def decodeN(n,N,D="0123456789qwertyuiopasdfghjklzxc"):
    return decodeN(n[:-1],N) * N + D.index(n[-1]) if n else 0

它似乎工作得很好:
print(encodeN(16002, 32))
# "ya2"
print(decodeN("ya2", 32))
# 16002
print(all(decodeN(encodeN(x, b), b) == x for b in range(2, 33) for x in range(10000)))
# True
print(all(encodeN(decodeN(str(x),32), 32) == str(x) for b in range(2, 33) for x in range(10000)))
# True

虽然这样做并不是非常高效。使用类似MartijnPieters的字典{{link1}}比使用str.index更好的想法。

2
不妨全力以赴,测试所有基数:print(all(decodeN(encodeN(x, b), b) == x for b in range(2, 33) for x in range(10000))) - Martijn Pieters
@MartijnPieters: 我正要编写那些测试,谢谢! - Eric Duminil

1
>>> import string
>>> len(string.readable)
100

根据这个,您可以达到最高100的基数,而不会出现重复字符或更改编码等问题。但是如果我们去掉\t\n\r\x0b\x0c,则只能达到94。
除此之外,您需要采用某些自定义规则,例如复制字符或添加前缀等。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接