如何将一个整数转换成任意进制的字符串?

323

Python允许通过给定的进制从字符串轻松创建整数,方法为

int(str, base). 

我希望进行反向操作:从整数中创建一个字符串,也就是说,我想要一些函数int2base(num, base),使得:

int(int2base(x, b), b) == x

函数名/参数顺序不重要。

对于任何数字x和基数b,只要int()接受即可。

这是一个很容易编写的函数:实际上比在这个问题中描述它更容易。但是,我感觉自己肯定漏掉了什么。

我知道有binocthex等函数,但由于以下几个原因,我不能使用它们:

  • 那些函数在旧版本的Python上不可用,而我需要与之兼容(2.2)

  • 我想要一种通用的解决方案,可以以相同的方式调用不同的基数

  • 我想允许使用除2、8、16以外的基数

相关


7
令人惊讶的是,没有人提供适用于任意大进制(1023)的解决方案。如果您需要,请查看我的解决方案,它适用于每个进制(从2到无穷大)。https://dev59.com/E3E95IYBdhLWcg3wi-ee#28666223 - Salvador Dali
我已经将任意大的进制解决方案作为基准纳入我的代码,并在同一函数中提供了反向转换。因此,如果有人感兴趣,请查看:https://dev59.com/E3E95IYBdhLWcg3wi-ee#71027453。 - Claudio
嗨,针对这个问题有一个叫做Basencode的库可供使用,请查看我的回答。希望能够帮到你! - Safwan Samsudeen
36个回答

223

令人惊讶的是,人们只提供了将其转换为小于英文字母表长度的小进制的解决方案。没有尝试给出任意进制从2到无限大的解决方案。

因此,这里有一个超级简单的解决方案:

def numberToBase(n, b):
    if n == 0:
        return [0]
    digits = []
    while n:
        digits.append(int(n % b))
        n //= b
    return digits[::-1]

因此,如果您需要将某个超级大的数字转换为基数为577,则执行numberToBase(67854 ** 15 - 102, 577)将给出正确的解决方案:[4, 473, 131, 96, 431, 285, 524, 486, 28, 23, 16, 82, 292, 538, 149, 25, 41, 483, 100, 517, 131, 28, 0, 435, 197, 264, 455]

以后您可以将其转换为任何基数。

  1. 在某个时刻,您会注意到有时没有内置库函数可以做您想要的事情,因此您需要编写自己的函数。如果您不同意,请发布您自己使用内置函数的解决方案,该函数可以将十进制数转换为基数为577的数字。
  2. 这是由于缺乏对某个基数下数字含义的理解。
  3. 我鼓励您思考一下为什么我的方法仅适用于n <= 36。一旦完成,我的函数返回列表并具有其特定签名也就显而易见了。

非常好的答案!很聪明将数字留在列表中;这使得更容易想出自己的字符表示法来表示不同进制的数字。 - Sylvester Kruin
2
如果您有一个包含0-9加上A-Z加上541个额外的Unicode字符的字符串,您将如何将该列表转换为单个字符表示形式? s =“0123456789ABCDEF” n =[15,1,13] "".join([s[x] for x in n]) - PhilHibbs
@PhilHibbs 当然可以,那种方法可行。当然,您需要决定使用哪些符号以及以什么顺序使用它们。 - Karl Knechtel
1
你可以扩展你的答案,包括基数1。如果b == 1:返回n * [1] - Jeff
嗨,回复第一点 - 我们确实有一个库可以做到这一点,请查看我的答案。感谢你宝贵的回答,我提供的答案只是对你的补充! - Safwan Samsudeen
显示剩余4条评论

126

如果您需要与古老版本的Python兼容,您可以使用gmpy(其中包括快速、完全通用的整数到字符串转换函数,并且可以构建用于这些古老版本-您可能需要尝试更早的版本,因为近期的版本未经测试,仅适用于较新的Python和GMP版本),或者,为了更方便但速度较慢,可以使用Python代码-例如,对于Python 2,最简单的方法是:

import string
digs = string.digits + string.ascii_letters


def int2base(x, base):
    if x < 0:
        sign = -1
    elif x == 0:
        return digs[0]
    else:
        sign = 1

    x *= sign
    digits = []

    while x:
        digits.append(digs[int(x % base)])
        x = int(x / base)

    if sign < 0:
        digits.append('-')

    digits.reverse()

    return ''.join(digits)

对于 Python 3,int(x / base) 会导致错误的结果,必须更改为 x // base

import string
digs = string.digits + string.ascii_letters


def int2base(x, base):
    if x < 0:
        sign = -1
    elif x == 0:
        return digs[0]
    else:
        sign = 1

    x *= sign
    digits = []

    while x:
        digits.append(digs[x % base])
        x = x // base

    if sign < 0:
        digits.append('-')

    digits.reverse()

    return ''.join(digits)

12
如果Alex所说的函数是gmpy2.digits(x, base),那么这里提及一下(使用gmpy2库)。 - mlvljr
2
有人提醒我,某些情况需要一个大于36的基数,因此digs应该是digs = string.digits + string.lowercase + string.uppercase - Paul
4
(或 string.digits + string.letters): or string.digits + string.letters - kojiro
4
为什么Python默认没有包含将N进制转换为字符串的函数?(在Javascript中有)是啥原因呢?虽然我们可以自己编写实现,但我在这个网站和其他地方搜索了很多实现方式,其中很多都存在缺陷。最好还是在核心发行版中包含一个经过测试、信誉良好的版本。 - Jason S
5
你也可以使用x //= base,它的行为类似于Python 2中的/=,可以去掉小数部分。这个答案应该包括一个声明,说明它是针对Python 2的。 - Noumenon
显示剩余12条评论

122
"{0:b}".format(100) # bin: 1100100
"{0:x}".format(100) # hex: 64
"{0:o}".format(100) # oct: 144

77
它只包括这三种碱基吗? - Thomas Ahle
4
抱歉,您无法指定自定义的整数进制。更多信息请参考这里:http://docs.python.org/library/string.html#formatstrings - Rost
3
0 是不必要的。这是 Python 2 文档链接:https://docs.python.org/2/library/string.html#format-string-syntax - Evgeni Sergeev
13
你可以使用 hex(100)[2:]oct(100)[2:]bin(100)[2:] 来获得相同的结果。 - Sassan
3
只有在使用2.7/3.1及以上版本时,这是不必要的。在2.6版本中,需要显式指定位置(或名称)。 - ShadowRanger
显示剩余2条评论

103
def baseN(num,b,numerals="0123456789abcdefghijklmnopqrstuvwxyz"):
    return ((num == 0) and numerals[0]) or (baseN(num // b, b, numerals).lstrip(numerals[0]) + numerals[num % b])

ref: http://code.activestate.com/recipes/65212/

请注意,这可能会导致
RuntimeError: maximum recursion depth exceeded in cmp

对于非常大的整数。

5
简洁而优雅。它似乎适用于 Python 2.2.3 的非负整数。负数会无限递归。 - Mark Borgerding
4
当基数 > numerals 的长度时,以及 num % b 值恰好 < numerals 的长度时,此代码会无声地失败。例如,尽管“numerals”字符串只有36个字符,baseN(60, 40)返回“1k”,而baseN(79, 40)会引发“IndexError”异常。这两个都应该引发某种错误。如果不满足条件2 <= base <= len(numerals),则应修改代码以引发错误。 - Chris Johnson
3
@osa,我的观点是代码的写法失败得非常糟糕(默默地产生误导性答案),而且可以很容易地修复。如果你是在说如果你能确定'b'不会超过'numerals'的长度,就不会出现错误,那么祝你好运。 - Chris Johnson
这里的代码最小,加1分。其他评论是正确的,它不能很好地处理一些边缘情况,但当您有已知、有限的基础和输入集,并且对第三方库的访问受限时,这是一个很好的替代品。 - EKW
3
在这里使用短路似乎过于令人困惑,为什么不直接使用if语句呢?这一行代码return numerals[0] if num == 0 else baseN(num // b, b, numerals).lstrip(numerals[0]) + numerals[num % b]同样简洁。 - Ian Hincks
显示剩余6条评论

81
>>> numpy.base_repr(10, base=3)
'101'

请注意,numpy.base_repr()函数的基数限制为36。否则将引发ValueError异常。


不错的解决方案。在我的情况下,我避免在 clac 中使用 numpy 是因为加载时间的考虑。预加载 numpy 会使简单表达式在 clac 中的运行时间增加三倍以上:例如 clac 1+1 的运行时间从约40毫秒增加到了140毫秒。 - Mark Borgerding
这符合内置的“int”函数的限制。更大的进制需要决定字母用完后该怎么办。 - plugwash
我猜这是大多数看到这个问题的人所寻找的。 - Pietro Battiston

35

递归

我会将最高票答案简化为:

base_string = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
def to_base(number, base): 
    return "0" if not number else to_base(number // base, base).lstrip("0") + base_string[number % base_string]

对于在非常大的整数和负数上出现的“RuntimeError: maximum recursion depth exceeded in cmp”错误,给出相同的建议。(您可以使用sys.setrecursionlimit(new_limit)

迭代

为了避免递归问题:

base_string = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
def to_base(number, base):
    result = ""
    while number:
        result += base_string[number % base]
        number //= base
    return result[::-1] or "0"

2
精美重构,无需库。 - Giampaolo Ferradini
停止条件不应该是return BS[0] if not n吗?以防万一您想使用像我一样的花式数字:) - Arnaud P
@ArnaudP同意。这个对我来说可行:如果n < b,则返回BS [n],否则返回to_base(n // b)+ BN [n%b] - Jens
1
在迭代方法中,也可以使用divmod函数,因为需要同时得到除数和余数,像这样number, remainder = divmod(number, base) - bbd108

22

很好的答案!我想我的问题的答案是“否”,我没有错过一些明显的解决方案。 以下是我将使用的函数,它压缩了答案中表达的好的想法。

  • 允许呼叫者提供字符映射(允许base64编码)
  • 检查负数和零
  • 将复数映射为字符串元组


def int2base(x,b,alphabet='0123456789abcdefghijklmnopqrstuvwxyz'):
    '将一个整数转换为给定进制的字符串表示'
    if b<2或b>len(alphabet):
        如果b==64:#假设是base64而不是引发错误
            字母表=“ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/”
        else:
            raise AssertionError("int2base base out of range")
    if isinstance(x,complex):#返回元组
        返回(int2base(x.real,b,alphabet),int2base(x.imag,b,alphabet))
    if x<=0:
        如果x == 0:
            返回字母表[0]
        其他:
            返回'-' + int2base(-x,b,alphabet)
    #其他x是非负实数
    rets =''
    while x>0:
        x,idx = divmod(x,b)
        rets =字母表[idx] + rets
    返回rets


5
如何将我们函数的base64输出转换回整数? - detly

15

你可以使用我的项目中的 baseconv.py

https://github.com/semente/python-baseconv

示例用法:

>>> from baseconv import BaseConverter
>>> base20 = BaseConverter('0123456789abcdefghij')
>>> base20.encode(1234)
'31e'
>>> base20.decode('31e')
'1234'
>>> base20.encode(-1234)
'-31e'
>>> base20.decode('-31e')
'-1234'
>>> base11 = BaseConverter('0123456789-', sign='$')
>>> base11.encode('$1234')
'$-22'
>>> base11.decode('$-22')
'$1234'

有一些内置的转换器,例如baseconv.base2baseconv.base16baseconv.base64


5
def base_conversion(num, base):
    digits = []
    while num > 0:
        num, remainder = divmod(num, base)
        digits.append(remainder)
    return digits[::-1]

通过将最后一行替换为 return ''.join(map(str, digits[::-1])),它在2到10进制之间更加有用。它不适用于基数1。 - Wolf
它也不能处理 num=0 - Wolf

5
def base(decimal ,base) :
    list = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    other_base = ""
    while decimal != 0 :
        other_base = list[decimal % base] + other_base
        decimal    = decimal / base
    if other_base == "":
        other_base = "0"
    return other_base

print base(31 ,16)

输出:

"1F"


other-base is the same as other - base, so you should use other_base - mbomb007
此外,如果 decimal 为零,则此方法无法正常工作。 - mbomb007

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