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 againnumconv / numconv.py
- commit
- dac783631be1
- parent
- 882b6d180761
- branch
- default
- tags
- 2.1
newline
1 |
a507bcdecc11
|
"""Convert strings to numbers and numbers to strings. |
2 |
a035ba7fb21b
|
|
3 |
a507bcdecc11
|
Gustavo Picon |
4 |
a507bcdecc11
|
http://code.tabo.pe/numconv/ |
5 |
ce5fa2cc27b2
|
|
6 |
a035ba7fb21b
|
""" |
7 |
a035ba7fb21b
|
|
8 |
bbef845a6732
|
|
9 |
882b6d180761
|
__version__ = '2.1.0' |
10 |
a035ba7fb21b
|
|
11 |
a035ba7fb21b
|
# from april fool's rfc 1924 |
12 |
a035ba7fb21b
|
BASE85 = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' \ |
13 |
a035ba7fb21b
|
'!#$%&()*+-;<=>?@^_`{|}~' |
14 |
a035ba7fb21b
|
|
15 |
a035ba7fb21b
|
# rfc4648 alphabets |
16 |
a035ba7fb21b
|
BASE16 = BASE85[:16] |
17 |
a035ba7fb21b
|
BASE32 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567' |
18 |
a035ba7fb21b
|
BASE32HEX = BASE85[:32] |
19 |
a035ba7fb21b
|
BASE64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' |
20 |
a035ba7fb21b
|
BASE64URL = BASE64[:62] + '-_' |
21 |
a035ba7fb21b
|
|
22 |
ce5fa2cc27b2
|
# http://en.wikipedia.org/wiki/Base_62 useful for url shorteners |
23 |
ce5fa2cc27b2
|
BASE62 = BASE85[:62] |
24 |
ce5fa2cc27b2
|
|
25 |
6ae83e41ff11
|
|
26 |
6ae83e41ff11
|
class NumConv(object): |
27 |
6ae83e41ff11
|
"""Class to create converter objects. |
28 |
b27cf772ba32
|
|
29 |
6ae83e41ff11
|
:param radix: The base that will be used in the conversions. |
30 |
6ae83e41ff11
|
The default value is 10 for decimal conversions. |
31 |
6ae83e41ff11
|
:param alphabet: A string that will be used as a encoding alphabet. |
32 |
b27cf772ba32
|
The length of the alphabet can be longer than the radix. In this |
33 |
b27cf772ba32
|
case the alphabet will be internally truncated. |
34 |
b27cf772ba32
|
|
35 |
6ae83e41ff11
|
The default value is :data:`numconv.BASE85` |
36 |
b27cf772ba32
|
|
37 |
6ae83e41ff11
|
:raise TypeError: when *radix* isn't an integer |
38 |
6ae83e41ff11
|
:raise ValueError: when *radix* is invalid |
39 |
6ae83e41ff11
|
:raise ValueError: when *alphabet* has duplicated characters |
40 |
6ae83e41ff11
|
""" |
41 |
b27cf772ba32
|
|
42 |
6ae83e41ff11
|
def __init__(self, radix=10, alphabet=BASE85): |
43 |
6ae83e41ff11
|
"basic validation and cached_map storage" |
44 |
6ae83e41ff11
|
if int(radix) != radix: |
45 |
6ae83e41ff11
|
raise TypeError('radix must be an integer') |
46 |
6ae83e41ff11
|
if not 2 <= radix <= len(alphabet): |
47 |
6ae83e41ff11
|
raise ValueError('radix must be >= 2 and <= %d' % ( |
48 |
6ae83e41ff11
|
len(alphabet), )) |
49 |
6ae83e41ff11
|
self.radix = radix |
50 |
6ae83e41ff11
|
self.alphabet = alphabet |
51 |
6ae83e41ff11
|
self.cached_map = dict(zip(self.alphabet, range(len(self.alphabet)))) |
52 |
6ae83e41ff11
|
if len(self.cached_map) != len(self.alphabet): |
53 |
6ae83e41ff11
|
raise ValueError("duplicate characters found in '%s'" % ( |
54 |
6ae83e41ff11
|
self.alphabet, )) |
55 |
6ae83e41ff11
|
|
56 |
6ae83e41ff11
|
def int2str(self, num): |
57 |
6ae83e41ff11
|
"""Converts an integer into a string. |
58 |
b27cf772ba32
|
|
59 |
6ae83e41ff11
|
:param num: A numeric value to be converted to another base as a |
60 |
6ae83e41ff11
|
string. |
61 |
b27cf772ba32
|
|
62 |
6ae83e41ff11
|
:rtype: string |
63 |
b27cf772ba32
|
|
64 |
6ae83e41ff11
|
:raise TypeError: when *num* isn't an integer |
65 |
6ae83e41ff11
|
:raise ValueError: when *num* isn't positive |
66 |
6ae83e41ff11
|
""" |
67 |
6ae83e41ff11
|
if int(num) != num: |
68 |
6ae83e41ff11
|
raise TypeError('number must be an integer') |
69 |
6ae83e41ff11
|
if num < 0: |
70 |
6ae83e41ff11
|
raise ValueError('number must be positive') |
71 |
6ae83e41ff11
|
radix, alphabet = self.radix, self.alphabet |
72 |
6ae83e41ff11
|
if radix in (8, 10, 16) and \ |
73 |
6ae83e41ff11
|
alphabet[:radix].lower() == BASE85[:radix].lower(): |
74 |
6ae83e41ff11
|
return ({8: '%o', 10: '%d', 16: '%x'}[radix] % num).upper() |
75 |
6ae83e41ff11
|
ret = '' |
76 |
6ae83e41ff11
|
while True: |
77 |
6ae83e41ff11
|
ret = alphabet[num % radix] + ret |
78 |
6ae83e41ff11
|
if num < radix: |
79 |
6ae83e41ff11
|
break |
80 |
6ae83e41ff11
|
num //= radix |
81 |
6ae83e41ff11
|
return ret |
82 |
b27cf772ba32
|
|
83 |
6ae83e41ff11
|
def str2int(self, num): |
84 |
6ae83e41ff11
|
"""Converts a string into an integer. |
85 |
b27cf772ba32
|
|
86 |
6ae83e41ff11
|
If possible, the built-in python conversion will be used for speed |
87 |
6ae83e41ff11
|
purposes. |
88 |
b27cf772ba32
|
|
89 |
6ae83e41ff11
|
:param num: A string that will be converted to an integer. |
90 |
b27cf772ba32
|
|
91 |
6ae83e41ff11
|
:rtype: integer |
92 |
b27cf772ba32
|
|
93 |
6ae83e41ff11
|
:raise ValueError: when *num* is invalid |
94 |
6ae83e41ff11
|
""" |
95 |
6ae83e41ff11
|
radix, alphabet = self.radix, self.alphabet |
96 |
6ae83e41ff11
|
if radix <= 36 and alphabet[:radix].lower() == BASE85[:radix].lower(): |
97 |
6ae83e41ff11
|
return int(num, radix) |
98 |
6ae83e41ff11
|
ret = 0 |
99 |
6ae83e41ff11
|
lalphabet = alphabet[:radix] |
100 |
6ae83e41ff11
|
for char in num: |
101 |
6ae83e41ff11
|
if char not in lalphabet: |
102 |
6ae83e41ff11
|
raise ValueError("invalid literal for radix2int() with radix " |
103 |
6ae83e41ff11
|
"%d: '%s'" % (radix, num)) |
104 |
6ae83e41ff11
|
ret = ret * radix + self.cached_map[char] |
105 |
6ae83e41ff11
|
return ret |
106 |
a035ba7fb21b
|
|
107 |
a035ba7fb21b
|
|
108 |
a035ba7fb21b
|
def int2str(num, radix=10, alphabet=BASE85): |
109 |
6ae83e41ff11
|
"helper for quick base conversions from integers to strings" |
110 |
6ae83e41ff11
|
return NumConv(radix, alphabet).int2str(num) |
111 |
0d8c44c9fa40
|
|
112 |
b27cf772ba32
|
|
113 |
a035ba7fb21b
|
def str2int(num, radix=10, alphabet=BASE85): |
114 |
6ae83e41ff11
|
"helper for quick base conversions from strings to integers" |
115 |
6ae83e41ff11
|
return NumConv(radix, alphabet).str2int(num) |