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