|
|
 |
|
Title: Numeric base converter that accepts arbitrary digits
Submitter: Drew Perttula
(other recipes)
Last Updated: 2002/01/31
Version no: 1.0
Category:
Algorithms
|
|
2 vote(s)
|
|
|
|
Description:
This is a traditional base converter with the twist that it
accepts any strings as the digits for the input and output bases.
Besides all the normal base-converts, you can now create compact
versions of huge numbers by converting them to a base that uses
all the letters and numbers for its digits, for example.
Source: Text Source
BASE2 = "01"
BASE10 = "0123456789"
BASE16 = "0123456789ABCDEF"
BASE62 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz"
def baseconvert(number,fromdigits,todigits):
""" converts a "number" between two bases of arbitrary digits
The input number is assumed to be a string of digits from the
fromdigits string (which is in order of smallest to largest
digit). The return value is a string of elements from todigits
(ordered in the same way). The input and output bases are
determined from the lengths of the digit strings. Negative
signs are passed through.
decimal to binary
>>> baseconvert(555,BASE10,BASE2)
'1000101011'
binary to decimal
>>> baseconvert('1000101011',BASE2,BASE10)
'555'
integer interpreted as binary and converted to decimal (!)
>>> baseconvert(1000101011,BASE2,BASE10)
'555'
base10 to base4
>>> baseconvert(99,BASE10,"0123")
'1203'
base4 to base5 (with alphabetic digits)
>>> baseconvert(1203,"0123","abcde")
'dee'
base5, alpha digits back to base 10
>>> baseconvert('dee',"abcde",BASE10)
'99'
decimal to a base that uses A-Z0-9a-z for its digits
>>> baseconvert(257938572394L,BASE10,BASE62)
'E78Lxik'
..convert back
>>> baseconvert('E78Lxik',BASE62,BASE10)
'257938572394'
binary to a base with words for digits (the function cannot convert this back)
>>> baseconvert('1101',BASE2,('Zero','One'))
'OneOneZeroOne'
"""
if str(number)[0]=='-':
number = str(number)[1:]
neg=1
else:
neg=0
x=long(0)
for digit in str(number):
x = x*len(fromdigits) + fromdigits.index(digit)
res=""
while x>0:
digit = x % len(todigits)
res = todigits[digit] + res
x /= len(todigits)
if neg:
res = "-"+res
return res
Discussion:
Motivation and application
I needed a system to include a reference number in an outgoing email, so that a reply email could be tracked to the original context. I'm betting that words in the subject line are the most likely to be returned in a reply email (as opposed to words in the body or headers), so I sought a way to compress/obfuscate my reference number into a word that would comfortably fit in the subject line. Now instead of 20020111152908/20020129120001, I can simply include "H6Xwq/IjmAf". It's shorter, less prone to tampering, and it looks more like a magic cookie than some MTA junk. In fact, I could boost the security even more by privately shuffling the order in which I use the letters as digits (instead of A==0, B==1, etc).
Other uses
I didn't find a base converter in python upon doing some quick searches, so perhaps this code would be useful for everyday conversions as well.
|
|
Add comment
|
|
Number of comments: 7
Does not handle non-zero systems, Not specified Not specified, 2003/02/11
This gives an incorrect answer for non-zero base systems. For instance, if BASE8=12345678 then decimal '9' should be octal '11'. Instead, it's given as '21'.
Add comment
confusion about "non-zero systems", Drew Perttula, 2003/05/18
The digits for BASE8 would be "01234567", not "12345678". With the latter set of digits, decimal 9 is properly output as '22' (octal, but not with the standard digits). The standard octal for decimal '9' is '11'.
Note that baseconvert("9","123456789","12345678") will output '21', but that input is not a decimal 9. It's a decimal 8.
Add comment
What abut OCT ?, Bojan Zagar, 2003/08/16
Python 2.3:
>>> baseconvert(9,"01234567890","01234567")
'11'
this is ok, but
than:
>>> baseconvert(10,"01234567890","01234567")
'13'
I thin, it should be 12 ??
Best regards.
Add comment
Sorry, Bojan Zagar, 2003/08/16
the BASE10 was wrong :)))
Add comment
further confirmation, s g, 2004/10/05
This problem appears to have something to do with the number of digits in the base10 number being converted.
In [28]: baseconvert(99,"01234567890","01234567")
Out[28]: '154'
In [29]: baseconvert(100,"01234567890","01234567")
Out[29]: '171'
Add comment
0 (Zero) does not work?, LesPaul Not specified, 2005/01/25
Been just trying baseconvert(0, BASE10, BASE62), it simply returns null string. A simple solution is to add:
if x == 0:
res = todigits[0]
just below the res="" line for the correct operation.
Add comment
you can inter-convert with Word-representations, Chris Smith, 2006/01/04
With a little re help, you can convert back from a word representation
like "OneZeroOne". I believe this will work as long as you use words
that start with a capital as you have done. The approach is to use an
alternation regex expression to split the number into "digits" and
then continue as you already do.
At the start of the def, put an "import re" statement and then replace
the "for digit in str(number)" with the following:
fromdigits=list(fromdigits)
pat=fromdigits[:]
pat.sort(lambda x,y:cmp(len(y),len(x))) #longest first
pat="(%s)" % '|'.join(pat)
number=[tmp for tmp in re.split(pat,str(number)) if tmp!='']
for digit in number:
Here is a demonstration:
###
>>> BASEWORDS=('Zero','One')
>>> BASE2='01'
>>> baseconvert('OneZeroOne',BASEWORDS,BASE2)
'101'
>>>
###
Add comment
|
|
|
|
|
 |
|