This repository was archived by the owner on Oct 1, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathbaseconv.py
166 lines (139 loc) · 4.76 KB
/
baseconv.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
# Copyright (c) 2010-2017 Guilherme Gondim. All rights reserved.
# Copyright (c) 2009 Simon Willison. All rights reserved.
# Copyright (c) 2002 Drew Perttula. All rights reserved.
#
# License:
# Python Software Foundation License version 2
#
# See the file "LICENSE" for terms & conditions for usage, and a DISCLAIMER OF
# ALL WARRANTIES.
#
# This Baseconv distribution contains no GNU General Public Licensed (GPLed)
# code so it may be used in proprietary projects just like prior ``baseconv``
# distributions.
#
# All trademarks referenced herein are property of their respective holders.
#
"""
Convert numbers from base 10 integers to base X strings and back again.
Example usage::
>>> from baseconv import base2, base16, base36, base56, base58, base62, base64
>>> base2.encode(1234)
'10011010010'
>>> base2.decode('10011010010')
'1234'
>>> base64.encode(100000000000000000000000000000000000L)
'4q9XSiTDWYk7Z-W00000'
>>> base64.decode('4q9XSiTDWYk7Z-W00000')
'100000000000000000000000000000000000'
>>> from baseconv import BaseConverter
>>> myconv = BaseConverter('MyOwnAlphabet0123456')
>>> repr(myconv)
"BaseConverter('MyOwnAlphabet0123456', sign='-')"
>>> myconv.encode('1234')
'wy1'
>>> myconv.decode('wy1')
'1234'
>>> myconv.encode(-1234)
'-wy1'
>>> myconv.decode('-wy1')
'-1234'
>>> altsign = BaseConverter('abcd-', sign='$')
>>> repr(altsign)
"BaseConverter('abcd-', sign='$')"
>>> altsign.encode(-1000000)
'$cc-aaaaaa'
>>> altsign.decode('$cc-aaaaaa')
'-1000000'
Exceptions::
>>> BaseConverter('')
Traceback (most recent call last):
...
ValueError: converter base digits length too short
>>> BaseConverter(digits='xyz-._', sign='-')
Traceback (most recent call last):
...
ValueError: sign character found in converter base digits
>>> base56.encode(3.14)
Traceback (most recent call last):
...
ValueError: invalid digit "."
>>> base58.decode('0IOl')
Traceback (most recent call last):
...
ValueError: invalid digit "0"
"""
__version__ = '1.2.2'
BASE2_ALPHABET = '01'
BASE16_ALPHABET = '0123456789ABCDEF'
BASE36_ALPHABET = '0123456789abcdefghijklmnopqrstuvwxyz'
BASE56_ALPHABET = '23456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnpqrstuvwxyz'
BASE58_ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
BASE62_ALPHABET = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
BASE64_ALPHABET = BASE62_ALPHABET + '-_'
class BaseConverter(object):
decimal_digits = '0123456789'
def __init__(self, digits, sign='-'):
self.sign = sign
self.digits = digits
if sign in self.digits:
raise ValueError('sign character found in converter base digits')
if len(self.digits) <= 1:
raise ValueError('converter base digits length too short')
def __repr__(self):
data = (self.__class__.__name__, self.digits, self.sign)
return "%s(%r, sign=%r)" % data
def _convert(self, number, from_digits, to_digits):
# make an integer out of the number
x = 0
for digit in str(number):
try:
x = x * len(from_digits) + from_digits.index(digit)
except ValueError:
raise ValueError('invalid digit "%s"' % digit)
# create the result in base 'len(to_digits)'
if x == 0:
res = to_digits[0]
else:
res = ''
while x > 0:
digit = x % len(to_digits)
res = to_digits[digit] + res
x = int(x // len(to_digits))
return res
def encode(self, number):
if str(number)[0] == '-':
neg = True
number = str(number)[1:]
else:
neg = False
value = self._convert(number, self.decimal_digits, self.digits)
if neg:
return self.sign + value
return value
def decode(self, number):
if str(number)[0] == self.sign:
neg = True
number = str(number)[1:]
else:
neg = False
value = self._convert(number, self.digits, self.decimal_digits)
if neg:
return '-' + value
return value
base2 = BaseConverter(BASE2_ALPHABET)
base16 = BaseConverter(BASE16_ALPHABET)
base36 = BaseConverter(BASE36_ALPHABET)
base56 = BaseConverter(BASE56_ALPHABET)
base58 = BaseConverter(BASE58_ALPHABET)
base62 = BaseConverter(BASE62_ALPHABET)
base64 = BaseConverter(BASE64_ALPHABET, sign='$')
if __name__ == '__main__':
# doctests
import doctest
doctest.testmod()
# other tests
nums = [-10 ** 10, 10 ** 10] + list(range(-100, 100))
for converter in [base2, base16, base36, base56, base58, base62, base64]:
for i in nums:
assert i == int(converter.decode(converter.encode(i))), '%s failed' % i