Python elegant inverse function of int(string,base)

后端 未结 11 851
谎友^
谎友^ 2020-11-28 06:01

python allows conversions from string to integer using any base in the range [2,36] using:

int(string,base)

im looking for an elegant inver

相关标签:
11条回答
  • 2020-11-28 06:21

    I had once written my own function with the same goal but is now embarrassingly complicated.

    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))
    

    I think though that the function strbase should only support bases >= 2 and <= 36 to prevent conflict with other tools in python such as int. Also, I think that only one case of alphabets should be used preferably uppercase again to prevent conflict with other functions like int since it will consider both "a" and "A" to be 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)
    
    0 讨论(0)
  • 2020-11-28 06:22

    This thread has some example implementations.

    Actually I think your solution looks rather nice, it's even recursive which is somehow pleasing here.

    I'd still simplify it to remove the else, but that's probably a personal style thing. I think if foo: return is very clear, and doesn't need an else after it to make it clear it's a separate branch.

    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)
    

    I simplified the 0-9 case in digit_to_char(), I think str() is clearer than the chr(ord()) construct. To maximize the symmetry with the >= 10 case an ord() could be factored out, but I didn't bother since it would add a line and brevity felt better. :)

    0 讨论(0)
  • 2020-11-28 06:22

    If you use Numpy, there is numpy.base_repr.

    You can read the code under numpy/core/numeric.py. Short and elegant

    0 讨论(0)
  • 2020-11-28 06:22

    Here is a recursive function:

    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')
    
    0 讨论(0)
  • 2020-11-28 06:27

    numpy.base_repr is a quite good solution, however, in some cases, we want a fixed-length string with leading zeros.

    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
    
    0 讨论(0)
提交回复
热议问题