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 again

tabo / numconv

Python library to convert strings to numbers and numbers to strings. Docs: http://docs.tabo.pe/numconv/tip/

Clone this repository (size: 168.1 KB): HTTPS / SSH
hg clone https://bitbucket.org/tabo/numconv
hg clone ssh://hg@bitbucket.org/tabo/numconv

numconv / 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)