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