python/base36.py
author Mike Crute <mcrute@gmail.com>
Wed Jun 09 15:12:43 2010 -0400 (23 months ago)
changeset 18 77fe8d4bcbd0
parent 13 68bb6da58cc2
permissions -rw-r--r--
Adding implements decorator
mcrute@0
     1
"""
mcrute@0
     2
Base36 Encoder/Decoder
mcrute@0
     3
by Mike Crute (mcrute@gmail.com) on August 26, 2008
mcrute@0
     4
This code has been placed in the public domain.
mcrute@0
     5
mcrute@0
     6
This is just a simple module to do base36 encoding and decoding. Theoretically
mcrute@0
     7
you could use this for any base < 2 <= 36 && base != 32 but I only really need 
mcrute@0
     8
to have base36 so its an exercise for the reader to implement other base 
mcrute@0
     9
conversions.
mcrute@0
    10
mcrute@0
    11
This won't work for base32 at least not if you want your output to work with other
mcrute@0
    12
base32 decoders because RFC4648 skips 0 and 1 (2-7A-Z) and this module doesn't
mcrute@0
    13
account for that. Bah...
mcrute@0
    14
mcrute@0
    15
The only reason I wrote this is because the other implementations of base
mcrute@0
    16
conversion relied upon hard coded lists of characters and that really 
mcrute@0
    17
bothered me, plus didn't quite give me the flexibility I was looking for.
mcrute@0
    18
"""
mcrute@0
    19
mcrute@0
    20
def _codec(str_in, base_from=36, base_to=10):
mcrute@0
    21
    """Convert a number to/from a base less than or equal to 36.
mcrute@0
    22
    Converts a string or number to or from a base without using static
mcrute@0
    23
    lookup tables.
mcrute@0
    24
    """
mcrute@0
    25
    # Some ASCII Codes
mcrute@0
    26
    ASCII = { "0": 48, "9": 57, "A": 65, "Z": 90 }
mcrute@0
    27
    
mcrute@0
    28
    # There are 8 characters between 9 and A
mcrute@0
    29
    from_digits = [chr(x) for x in range(ASCII["0"], ASCII["9"] + 8 + base_from) 
mcrute@0
    30
                            if (x >= ASCII["0"] and x <= ASCII["9"]) or 
mcrute@0
    31
                               (x >= ASCII["A"] and x <= ASCII["Z"])][:base_from]
mcrute@0
    32
                               
mcrute@0
    33
    to_digits = [chr(x) for x in range(ASCII["0"], ASCII["9"] + 8 + base_to) 
mcrute@0
    34
                            if (x >= ASCII["0"] and x <= ASCII["9"]) or 
mcrute@0
    35
                               (x >= ASCII["A"] and x <= ASCII["Z"])][:base_to]
mcrute@0
    36
    
mcrute@0
    37
    x = long(0)
mcrute@0
    38
    for digit in str(str_in).upper():
mcrute@0
    39
        x = x * len(from_digits) + from_digits.index(digit)
mcrute@0
    40
mcrute@0
    41
    result = ""
mcrute@0
    42
    # This is going to assemble our number in reverse order
mcrute@0
    43
    # so we'll have to fix it before we return it
mcrute@0
    44
    while x > 0:
mcrute@0
    45
        result += to_digits[x % len(to_digits)]
mcrute@0
    46
        x /= len(to_digits)
mcrute@0
    47
        
mcrute@0
    48
    return result[::-1]
mcrute@0
    49
    
mcrute@0
    50
    
mcrute@0
    51
def base36encode(str_in):
mcrute@0
    52
    """Base36 encode a base10 number.
mcrute@0
    53
    """
mcrute@0
    54
    return _codec(str_in, 10, 36)
mcrute@0
    55
    
mcrute@0
    56
def base36encode(str_in):
mcrute@0
    57
    """Get a base10 number for a base36 number.
mcrute@0
    58
    """
mcrute@0
    59
    return long(_codec(str_in, 36, 10))