web-dev-qa-db-fra.com

Comment convertir un entier de n'importe quelle base en chaîne?

Python permet de créer facilement un entier à partir d’une chaîne d’une base donnée via 

int(str, base). 

Je veux effectuer l'inverse: création d'une chaîne à partir d'un entier, C'est-à-dire. Je veux une fonction int2base(num, base), telle que:

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

Le nom de la fonction/ordre des arguments est sans importance. 

Pour tout nombre x et base b, int() acceptera.

C'est une fonction facile à écrire: en fait, c'est plus facile que de la décrire dans cette question. Cependant, je sens qu'il me manque quelque chose. 

Je connais les fonctions bin, oct, hex, mais je ne peux pas les utiliser pour plusieurs raisons:

  • Ces fonctions ne sont pas disponibles sur les anciennes versions de Python, avec lesquelles j'ai besoin de compatibilité avec (2.2) 

  • Je veux une solution générale qui puisse s'appeler de la même manière pour différentes bases

  • Je veux autoriser des bases autres que 2, 8, 16

En relation

172
Mark Borgerding

Si vous avez besoin de compatibilité avec les versions anciennes de Python, vous pouvez utiliser gmpy (qui inclut une fonction de conversion rapide et complètement générale de chaîne à chaîne, et peut être construit pour de telles versions anciennes - vous devrez peut-être essayez des versions plus anciennes, car les dernières versions n’ont pas été testées pour les versions vénérées de Python et GMP (seulement les plus récentes), ou, pour des raisons de rapidité et de commodité, utilisez du code Python - par exemple, plus simplement:

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)
84
Alex Martelli
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/

S'il vous plaît être conscient que cela peut conduire à 

RuntimeError: maximum recursion depth exceeded in cmp

pour de très grands entiers.

85
jellyfishtree

Étonnamment, les gens ne donnaient que des solutions qui se convertissaient en petites bases (inférieures à la longueur de l'alphabet anglais). Il n'y avait aucune tentative de donner une solution qui convertit en une base arbitraire de 2 à l'infini.

Alors voici une solution super simple:

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

donc si vous avez besoin de convertir un nombre super énorme en 577 de base,

numberToBase(67854 ** 15 - 102, 577), vous donnera une solution correcte: [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],

Que vous pourrez convertir plus tard en base de votre choix

79
Salvador Dali
"{0:b}".format(100) # bin: 1100100
"{0:x}".format(100) # hex: 64
"{0:o}".format(100) # oct: 144
67
Rost

Excellentes réponses! Je suppose que la réponse à ma question était "non". Voici la fonction que je vais utiliser qui condense les bonnes idées exprimées dans les réponses.

  • autoriser le mappage de caractères fourni par l'appelant (autorise l'encodage en base64)
  • vérifie négatif et zéro
  • mappe des nombres complexes en tuples de chaînes


def int2base(x,b,alphabet='0123456789abcdefghijklmnopqrstuvwxyz'):
    'convert an integer to its string representation in a given base'
    if b<2 or b>len(alphabet):
        if b==64: # assume base64 rather than raise error
            alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
        else:
            raise AssertionError("int2base base out of range")
    if isinstance(x,complex): # return a Tuple
        return ( int2base(x.real,b,alphabet) , int2base(x.imag,b,alphabet) )
    if x<=0:
        if x==0:
            return alphabet[0]
        else:
            return  '-' + int2base(-x,b,alphabet)
    # else x is non-negative real
    rets=''
    while x>0:
        x,idx = divmod(x,b)
        rets = alphabet[idx] + rets
    return rets

20
Mark Borgerding

Python n'a pas de fonction intégrée pour imprimer un entier dans une base arbitraire. Vous devrez écrire le vôtre si vous le souhaitez.

14
Mike Graham

Vous pouvez utiliser baseconv.py de mon projet: https://github.com/semente/python-baseconv

Exemple d'utilisation:

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

Il existe des convertisseurs bultin comme par exemple baseconv.base2, baseconv.base16 et baseconv.base64.

13
semente

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

def base10toN(num,n):
    """Change a  to a base-n number.
    Up to base-36 is supported without special notation."""
    num_rep={10:'a',
         11:'b',
         12:'c',
         13:'d',
         14:'e',
         15:'f',
         16:'g',
         17:'h',
         18:'i',
         19:'j',
         20:'k',
         21:'l',
         22:'m',
         23:'n',
         24:'o',
         25:'p',
         26:'q',
         27:'r',
         28:'s',
         29:'t',
         30:'u',
         31:'v',
         32:'w',
         33:'x',
         34:'y',
         35:'z'}
    new_num_string=''
    current=num
    while current!=0:
        remainder=current%n
        if 36>remainder>9:
            remainder_string=num_rep[remainder]
        Elif remainder>=36:
            remainder_string='('+str(remainder)+')'
        else:
            remainder_string=str(remainder)
        new_num_string=remainder_string+new_num_string
        current=current/n
    return new_num_string

Voici un autre du même lien

def baseconvert(n, base):
    """convert positive decimal integer n to equivalent in another base (2-36)"""

    digits = "0123456789abcdefghijklmnopqrstuvwxyz"

    try:
        n = int(n)
        base = int(base)
    except:
        return ""

    if n < 0 or base < 2 or base > 36:
        return ""

    s = ""
    while 1:
        r = n % base
        s = digits[r] + s
        n = n / base
        if n == 0:
            break

    return s
4
John La Rooy

J'ai fait un paquet pip pour cela.

Je vous recommande d’utiliser mon bases.py https://github.com/kamijoutouma/bases.py qui s’est inspiré de bases.js

from bases import Bases
bases = Bases()

bases.toBase16(200)                // => 'c8'
bases.toBase(200, 16)              // => 'c8'
bases.toBase62(99999)              // => 'q0T'
bases.toBase(200, 62)              // => 'q0T'
bases.toAlphabet(300, 'aAbBcC')    // => 'Abba'

bases.fromBase16('c8')               // => 200
bases.fromBase('c8', 16)             // => 200
bases.fromBase62('q0T')              // => 99999
bases.fromBase('q0T', 62)            // => 99999
bases.fromAlphabet('Abba', 'aAbBcC') // => 300

se référer à https://github.com/kamijoutouma/bases.py#known-basesalphabets pour connaître les bases utilisables

EDIT: Pip link https://pypi.python.org/pypi/bases.py/0.2.2

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

print base(31 ,16)

sortie:

"1F"

2
mukundan

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

2
V. Ayrat

Récursif

Je voudrais simplifier le réponse la plus votée pour:

BS="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
def to_base(n, b): 
    return "0" if not n else to_base(n//b, b).lstrip("0") + BS[n%b]

Avec le même conseil pour RuntimeError: maximum recursion depth exceeded in cmp sur des entiers très grands et des nombres négatifs. (Vous pouvez utiliser sys.setrecursionlimit(new_limit) )

Itératif

Pour éviter les problèmes de récursivité:

BS="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
def to_base(s, b):
    res = ""
    while s:
        res+=BS[s%b]
        s//= b
    return res[::-1] or "0"
2
M.M
>>> import string
>>> def int2base(integer, base):
        if not integer: return '0'
        sign = 1 if integer > 0 else -1
        alphanum = string.digits + string.ascii_lowercase
        nums = alphanum[:base]
        res = ''
        integer *= sign
        while integer:
                integer, mod = divmod(integer, base)
                res += nums[mod]
        return ('' if sign == 1 else '-') + res[::-1]


>>> int2base(-15645, 23)
'-16d5'
>>> int2base(213, 21)
'a3'
1
SilentGhost
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()

explication

Dans toute base, chaque nombre est égal à a1+a2*base**2+a3*base**3... La "mission" est de trouver tous les a.

Pour chaqueN=1,2,3..., le code isole le aN*base**N en "mouduling" par b pour b=base**(N+1) qui tranche tous les a supérieurs à N, et découpant tous les a dont la série est inférieure à N en diminuant a chaque fois que aN*base**N actuel. 

Base% (base-1) == 1 pour cette base ** p% (base-1) == 1 et donc q * base ^ p% (base-1) == q avec une seule exception lorsque q = base-1 qui retourne 0 . Pour corriger ça au cas ou elle retourne 0, la fonction vérifie si elle est 0 depuis le début. 


les avantages

dans cet échantillon, il n'y a qu'une multiplication (au lieu de la division) et quelques moudues, ce qui prend relativement peu de temps.

1
Shu ba

Une solution récursive pour les intéressés. Bien sûr, cela ne fonctionnera pas avec des valeurs binaires négatives. Vous auriez besoin de mettre en œuvre le complément de deux.

def generateBase36Alphabet():
    return ''.join([str(i) for i in range(10)]+[chr(i+65) for i in range(26)])

def generateAlphabet(base):
    return generateBase36Alphabet()[:base]

def intToStr(n, base, alphabet):
    def toStr(n, base, alphabet):
        return alphabet[n] if n < base else toStr(n//base,base,alphabet) + alphabet[n%base]
    return ('-' if n < 0 else '') + toStr(abs(n), base, alphabet)

print('{} -> {}'.format(-31, intToStr(-31, 16, generateAlphabet(16)))) # -31 -> -1F
1
Mr. Polywhirl

Un autre court (et plus facile à comprendre imo):

def int_to_str(n, b, symbols='0123456789abcdefghijklmnopqrstuvwxyz'):
    return (int_to_str(n/b, b, symbols) if n >= b else "") + symbols[n%b]

Et avec une gestion appropriée des exceptions:

def int_to_str(n, b, symbols='0123456789abcdefghijklmnopqrstuvwxyz'):
    try:
        return (int_to_str(n/b, b) if n >= b else "") + symbols[n%b]
    except IndexError:
        raise ValueError(
            "The symbols provided are not enough to represent this number in "
            "this base")
0
Ariel

Voici une version récursive qui gère les entiers signés et les chiffres personnalisés.

import string

def base_convert(x, base, digits=None):
    """Convert integer `x` from base 10 to base `base` using `digits` characters as digits.
    If `digits` is omitted, it will use decimal digits + lowercase letters + uppercase letters.
    """
    digits = digits or (string.digits + string.ascii_letters)
    assert 2 <= base <= len(digits), "Unsupported base: {}".format(base)
    if x == 0:
        return digits[0]
    sign = '-' if x < 0 else ''
    x = abs(x)
    first_digits = base_convert(x // base, base, digits).lstrip(digits[0])
    return sign + first_digits + digits[x % base]
0
Antoine Pinsard
def baseConverter(x, b):
    s = ""
    d = string.printable.upper()
    while x > 0:
        s += d[x%b]
        x = x / b
    return s[::-1]
0
gjivanya
num = input("number")
power = 0
num = int(num)
while num > 10:
    num = num / 10
    power += 1

print(str(round(num, 2)) + "^" + str(power))
0
Casey Howard
def dec_to_radix(input, to_radix=2, power=None):
    if not isinstance(input, int):
        raise TypeError('Not an integer!')
    Elif power is None:
        power = 1

    if input == 0:
        return 0
    else:
        remainder = input % to_radix**power
        digit = str(int(remainder/to_radix**(power-1)))
        return int(str(dec_to_radix(input-remainder, to_radix, power+1)) + digit)

def radix_to_dec(input, from_radix):
    if not isinstance(input, int):
        raise TypeError('Not an integer!')
    return sum(int(digit)*(from_radix**power) for power, digit in enumerate(str(input)[::-1]))

def radix_to_radix(input, from_radix=10, to_radix=2, power=None):
    dec = radix_to_dec(input, from_radix)
    return dec_to_radix(dec, to_radix, power)
0
nameisnotphil

Une autre solution, fonctionne avec les bases 2 à 10, nécessite une modification pour les bases supérieures:

def n2b(n, b):
    if n == 0:
        return 0
    d = []
    while n:
        d.append(int(n % b))
        n /= b
    return ''.join(map(str,d[::-1]))

Exemple:

n2b(10,2) => '10100'
int(n2b(10,2),2) => 10
0
Stanislav

Les chaînes ne sont pas le seul choix possible pour représenter des nombres: vous pouvez utiliser une liste d'entiers pour représenter l'ordre de chaque chiffre. Ceux-ci peuvent facilement être convertis en chaîne.

Aucune des réponses ne rejette la base <2; et la plupart fonctionneront très lentement ou planteront avec des débordements de pile pour des nombres très grands (tels que 56789 ** 43210). Pour éviter de tels échecs, réduisez rapidement comme ceci:

def n_to_base(n, b):
    if b < 2: raise # invalid base
    if abs(n) < b: return [n]
    ret = [y for d in n_to_base(n, b*b) for y in divmod(d, b)]
    return ret[1:] if ret[0] == 0 else ret # remove leading zeros

def base_to_n(v, b):
    h = len(v) // 2
    if h == 0: return v[0]
    return base_to_n(v[:-h], b) * (b**h) + base_to_n(v[-h:], b)

assert ''.join(['0123456789'[x] for x in n_to_base(56789**43210,10)])==str(56789**43210)

Speedwise, n_to_base est comparable à str pour les grands nombres (environ 0,3 s sur ma machine), mais si vous comparez contre hex, vous serez peut-être surpris (environ 0,3 ms sur ma machine). 1000x plus vite). La raison en est que le grand entier est stocké en mémoire en base 256 (octets). Chaque octet peut simplement être converti en chaîne hexagonale de deux caractères. Cet alignement n’est valable que pour les bases de puissance deux, c’est pourquoi il existe des cas spéciaux pour 2,8 et 16 (et base64, ascii, utf16, utf32).

Considérons le dernier chiffre d'une chaîne décimale. Comment se rapporte-t-il à la séquence d'octets qui forme son entier? Étiquetons les octets s[i] avec s[0] étant le moins significatif (little endian). Alors le dernier chiffre est sum([s[i]*(256**i) % 10 for i in range(n)]). Eh bien, il arrive que 256 ** i se termine par un 6 pour i> 0 (6 * 6 = 36) de sorte que le dernier chiffre est (s[0]*5 + sum(s)*6)%10. De là, vous pouvez voir que le dernier chiffre dépend de la somme de tous les octets. Cette propriété non locale est ce qui rend la conversion en décimale plus difficile.

0
colski