Gustavo Picon is sharing code with you
Bitbucket is a code hosting site. Unlimited public and private repositories. Free for small teams.
Don't show this againdjango-treebeard / treebeard / numconv.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 | # -*- coding: utf-8 -*-
"""
numconv
-------
:synopsys: Python library to convert strings to numbers and numbers to
strings.
:copyright: 2008 by Gustavo Picon
:license: Apache License 2.0
:version: 1.2.0
:url: http://code.google.com/p/numconv/
:documentation:
`numconv-docs
<http://numconv.googlecode.com/svn/docs/index.html>`_
:examples:
`numconv-tests
<http://code.google.com/p/numconv/source/browse/trunk/tests.py>`_
:mod:`numconv` converts a string into a number and a number into a string
using default or user supplied encoding alphabets.
constants
~~~~~~~~~
.. data:: BASE85
Alphabet defined in section 4 of :rfc:`1924`. Supposed to be a joke (it is
an April's fools RFC after all), but is quite useful because it can be used
as a base for the most common numeric conversions.
.. data:: BASE16
BASE32
BASE32HEX
BASE64
BASE64URL
Alphabets defined in :rfc:`4648`. Not really for common numeric conversion
use.
"""
__version__ = '1.2.0'
# from april fool's rfc 1924
BASE85 = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' \
'!#$%&()*+-;<=>?@^_`{|}~'
# rfc4648 alphabets
BASE16 = BASE85[:16]
BASE32 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'
BASE32HEX = BASE85[:32]
BASE64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
BASE64URL = BASE64[:62] + '-_'
# cached maps
CMAPS = {}
def int2str(num, radix=10, alphabet=BASE85):
"""Converts an integer into a string.
:param num: A numeric value to be converted to another base as a string.
:param radix: The base that will be used in the conversion.
The default value is 10 for decimal conversion.
:param alphabet: A string that will be used as a encoding alphabet.
The length of the alphabet can be longer than the radix. In this case
the alphabet will be internally truncated.
The default value is :data:`numconv.BASE85`
:rtype: string
:raise TypeError: when *num* isn't an integer
:raise ValueError: when *num* isn't positive
:raise TypeError: when *radix* isn't an integer
:raise ValueError: when *radix* is invalid
:raise ValueError: when *alphabet* has duplicated characters
**Examples** (taken from :file:`tests.py`):
3735928559 to hexadecimal::
>> numconv.int2str(3735928559, 16)
'DEADBEEF'
10284 to binary::
>> numconv.int2str(19284, 2)
'100101101010100'
37 to base 4 using a custom dictionary::
>> numconv.int2str(37, 4, 'rofl')
'foo'
Very large number to :data:`~numconv.BASE85`::
>> numconv.int2str(2693233728041137L, 85)
'~123AFz@'
"""
if alphabet not in CMAPS:
# just to validate the alphabet
getcmap(alphabet)
if int(num) != num:
raise TypeError('number must be an integer')
if num < 0:
raise ValueError('number must be positive')
if int(radix) != radix:
raise TypeError('radix must be an integer')
if not 2 <= radix <= len(alphabet):
raise ValueError('radix must be >= 2 and <= %d' % (len(alphabet), ))
if radix in (8, 10, 16) and \
alphabet[:radix].lower() == BASE85[:radix].lower():
return ({8: '%o', 10: '%d', 16: '%x'}[radix] % num).upper()
ret = ''
while True:
ret = alphabet[num % radix] + ret
if num < radix:
break
num //= radix
return ret
def str2int(num, radix=10, alphabet=BASE85):
"""Converts a string into an integer.
If possible, the built-in python conversion will be used for speed
porpuses.
:param num: A string that will be converted to an integer.
:param radix: The base that will be used in the conversion.
The default value is 10 for decimal conversion.
:param alphabet: A string that will be used as a encoding alphabet.
The length of the alphabet can be longer than the radix. In this case
the alphabet will be internally truncated.
The default value is :data:`numconv.BASE85`
:rtype: integer
:raise TypeError: when *radix* isn't an integer
:raise ValueError: when *radix* is invalid
:raise ValueError: when *num* is invalid
:raise ValueError: when *alphabet* has duplicated characters
**Examples** (taken from :file:`tests.py`):
Hexadecimal 'DEADBEEF' to integer::
>> numconv.str2int('DEADBEEF', 16)
3735928559L
Binary '100101101010100' to integer::
>> numconv.str2int('100101101010100', 2)
19284
Base 4 with custom encoding 'foo' to integer::
>> numconv.str2int('foo', 4, 'rofl')
37
:data:`~numconv.BASE85` '~123AFz@' to integer::
>> numconv.str2int('~123AFz@', 85)
2693233728041137L
"""
if alphabet not in CMAPS:
getcmap(alphabet)
if int(radix) != radix:
raise TypeError('radix must be an integer')
if not 2 <= radix <= len(alphabet):
raise ValueError('radix must be >= 2 and <= %d' % (len(alphabet), ))
if radix <= 36 and alphabet[:radix].lower() == BASE85[:radix].lower():
return int(num, radix)
ret = 0
lmap = CMAPS[alphabet]
lalphabet = alphabet[:radix]
for char in num:
if char not in lalphabet:
raise ValueError("invalid literal for radix2int() with radix "
"%d: '%s'" % (radix, num))
ret = ret * radix + lmap[char]
return ret
def getcmap(alphabet):
"""Builds an internal alphabet lookup table, to be stored in CMAPS"""
ret = dict(zip(alphabet, range(len(alphabet))))
if len(ret) != len(alphabet):
raise ValueError("duplicate characters found in '%s'" % (alphabet, ))
CMAPS[alphabet] = ret
return ret
|