Python优雅的反函数int(字符串,基数)

时间:2010-01-14 10:28:04

标签: python

python允许使用范围[2,36]中的任何基数使用:

将字符串转换为整数
int(string,base)

我正在寻找一个优雅的反函数,它接受一个整数和一个基数并返回一个字符串

例如

>>> str_base(224,15)
'ee'

我有以下解决方案:

def digit_to_char(digit):
    if digit < 10: return chr(ord('0') + digit)
    else: return chr(ord('a') + digit - 10)

def str_base(number,base):
    if number < 0:
        return '-' + str_base(-number,base)
    else:
        (d,m) = divmod(number,base)
        if d:
            return str_base(d,base) + digit_to_char(m)
        else:
            return digit_to_char(m)

注意:digit_to_char()适用于基数&lt; = 169,任意使用ascii字符后'z'作为36以上碱基的数字

是否有python内置函数,库函数或更优雅的int函数(字符串,基数)?

11 个答案:

答案 0 :(得分:30)

也许这不应该是一个答案,但它可能对某些人有所帮助:内置的format函数确实在几个基础上将数字转换为字符串:

>>> format(255, 'b') # base 2
'11111111'
>>> format(255, 'd') # base 10
'255'
>>> format(255, 'o') # base 8
'377'
>>> format(255, 'x') # base 16
'ff'

答案 1 :(得分:18)

如果您使用Numpy,则有numpy.base_repr

您可以阅读numpy/core/numeric.py下的代码。短而优雅

答案 2 :(得分:11)

This thread有一些示例实现。

实际上我认为你的解决方案看起来相当不错,它甚至是递归的,这在某种程度上令人满意。

我仍然会简化它以删除else,但这可能是个人风格的事情。我认为if foo: return非常清楚,并且在它之后不需要else来表明它是一个单独的分支。

def digit_to_char(digit):
    if digit < 10:
        return str(digit)
    return chr(ord('a') + digit - 10)

def str_base(number,base):
    if number < 0:
        return '-' + str_base(-number, base)
    (d, m) = divmod(number, base)
    if d > 0:
        return str_base(d, base) + digit_to_char(m)
    return digit_to_char(m)

我简化了digit_to_char()中的0-9案例,我认为str()chr(ord())构造更清晰。为了最大化>= 10情况下的对称性,可以考虑ord(),但我没有打扰,因为它会增加一条线,简洁感更好。 :)

答案 3 :(得分:6)

以上答案非常好。它帮助我制作了一个我必须在C中实现的算法的原型。

我想提出一点改变(我用过)将十进制转换为符号空间的基础

我也忽略了否定价值只是为了简短和数学不正确的事实 - &GT;模块化算术的其他规则 - &GT;其他数学如果你使用二进制,八进制或十六进制 - &gt; diff in unsigned&amp;签名值

def str_base(number, base):
   (d,m) = divmod(number,len(base))
   if d > 0:
      return str_base(d,base)+base[m]
   return base[m]

导致以下输出

>>> str_base(13,'01')
'1101'
>>> str_base(255,'01')
'11111111'
>>> str_base(255,'01234567')
'377'
>>> str_base(255,'0123456789')
'255'
>>> str_base(255,'0123456789abcdef')
'ff'
>>> str_base(1399871903,'_helowrd')
'hello_world'

如果你想使用propper零符号填充,你可以使用

symbol_space = 'abcdest'

>>> str_base(734,symbol_space).rjust(0,symbol_space[0])
'catt'
>>> str_base(734,symbol_space).rjust(6,symbol_space[0])
'aacatt'

答案 4 :(得分:4)

回顾一下。

def int2str(num, base=16, sbl=None):
    if not sbl:
        sbl = '0123456789abcdefghijklmnopqrstuvwxyz'
    if len(sbl) < 2:
        raise ValueError, 'size of symbols should be >= 2'
    if base < 2 or base > len(sbl):
        raise ValueError, 'base must be in range 2-%d' % (len(sbl))

    neg = False
    if num < 0:
        neg = True
        num = -num

    num, rem = divmod(num, base)
    ret = ''
    while num:
        ret = sbl[rem] + ret
        num, rem = divmod(num, base)
    ret = ('-' if neg else '') + sbl[rem] + ret

    return ret

答案 5 :(得分:2)

digit_to_char可以像这样实现:

def digit_to_char(digit):
    return (string.digits + string.lowercase)[digit]

答案 6 :(得分:2)

我曾经以同样的目标编写了自己的函数,但现在却非常复杂。

from math import log, ceil, floor
from collections import deque
from itertools import repeat
from string import uppercase, digits
import re

__alphanumerals = (digits + uppercase)

class InvalidBaseError(ValueError): pass
class FloatConvertError(ValueError): pass
class IncorrectBaseError(ValueError): pass

def getbase(number, base=2, frombase = 10):
    if not frombase == 10:
        number = getvalue(number, frombase)
        #getvalue is also a personal function to replicate int(number, base)

    if 1 >= base or base >= len(__alphanumerals) or not floor(base) == base:
        raise InvalidBaseError("Invalid value: {} entered as base to convert
          to. \n{}".format(base,
        "Assert that the base to convert to is a decimal integer."))

    if isinstance(number, str):
        try:
            number = atof(number)
        except ValueError:
            #The first check of whether the base is 10 would have already corrected the number
            raise IncorrectBaseError("Incorrect base passed as base of number -> number: {} base: {}".format(number, frombase))
    #^ v was supporting float numbers incase number was the return of another operation
    if number > floor(number):
        raise FloatConvertError("The number to be converted must not be a float. {}".format(number))

    isNegative = False
    if number < 0:
        isNegative = True
        number = abs(number)

    logarithm = log(number, base) if number else 0 #get around number being zero easily

    ceiling = int(logarithm) + 1

    structure = deque(repeat(0, ceiling), maxlen = ceiling)

    while number:
        if number >= (base ** int(logarithm)):
            acceptable_digit = int(number / (base ** floor(logarithm)))
            structure.append(acceptable_digit if acceptable_digit < 10 else     __alphanumerals[acceptable_digit])
            number -= acceptable_digit * (base ** floor(logarithm))
        else:
            structure.append(0)

        logarithm -= 1

    while structure[0] == 0:
        #the result needs trailing zeros
        structure.rotate(-1)

    return ("-" if isNegative and number else "") + reduce(lambda a, b: a + b, map(lambda a: str(a), structure))

我认为strbase函数应该只支持base&gt; = 2和&lt; = 36以防止与python中的其他工具冲突,例如int。 此外,我认为只应使用一个字母大小写字母,最好再大写,以防止与其他函数如int冲突,因为它会将“a”和“A”都视为10。

from string import uppercase

dig_to_chr = lambda num: str(num) if num < 10 else uppercase[num - 10]

def strbase(number, base):
    if not 2 <= base <= 36:
        raise ValueError("Base to convert to must be >= 2 and <= 36")

    if number < 0:
        return "-" + strbase(-number, base)

    d, m = divmod(number, base)
    if d:
        return strbase(d, base) + dig_to_chr(m)

    return dig_to_chr(m)

答案 7 :(得分:0)

这是我的解决方案:

def int2base(a, base, numerals="0123456789abcdefghijklmnopqrstuvwxyz"):
     baseit = lambda a=a, b=base: (not a) and numerals[0] or baseit(a-a%b,b*base)+numerals[a%b%(base-1) or (a%b) and (base-1)]
     return baseit()

说明

在任何基数中,每个数字都等于a1+a2*base**2+a3*base**3...。 “使命”是找到所有的。

对于每个N=1,2,3...,代码将aN*base**N通过“mouduling”隔离为b b=base**(N+1)将所有大于N的切片隔离,并切割所有a的序列为每当函数被当前aN*base**N调用时,通过减小a来小于N.

因此,当Base%(base-1)==1返回base**p%(base-1)==1时,{p> q*base^p%(base-1)==q q=base-1因此0只有一个例外。 要解决这个问题,如果它返回0,该函数将从头开始检查0

优点

在这个样本中,只有一个乘法(而不是除法)和一些模数实例需要相对较少的时间。

答案 8 :(得分:0)

看起来这可能是我发光的时候了。信不信由你,以下是我近三年前写的一些移植和修改的Scratch代码,看看我能用多快的速度从denary转换为十六进制。

简单地说,它首先采用整数,基数和可选的附带数字串,然后计算从最低有效数开始的转换整数的每个数字。

def int2base(num, base, abc="0123456789abcdefghijklmnopqrstuvwxyz"):
  if num < 0:
    return '-' + int2base(-num, base, abc)

  else:
    output = abc[num % base] # rightmost digit

    while num >= base:
      num //= base # move to next digit to the left
      output = abc[num % base] + output # this digit

    return output

在我自己的PC上,此代码能够使用输入范围0-9999和基数36完成1000万次迭代,始终低于5秒。 使用相同的测试,我发现至少比其他任何答案快4秒。

>>> timeit.timeit(lambda: [int2base(n, 36) for n in range(10000)], number=1000)
4.883068453882515

答案 9 :(得分:0)

numpy.base_repr是一个很好的解决方案,但是,在某些情况下,我们希望使用带有前导零的固定长度字符串。

def base_repr(x: int, base: int, length: int):
    from numpy import base_repr
    from math import log, ceil
    s = base_repr(x, base, length - ceil(log(x, base)))
    return s

答案 10 :(得分:0)

这是一个递归函数:

def encode(nIn, nBase):
   n = nIn // nBase
   s = '0123456789abcdefghijklmnopqrstuvwxyz'[nIn % nBase]
   return encode(n, nBase) + s if n > 0 else s

n = 1577858399
s = encode(n, 36)
print(s == 'q3ezbz')