tags:

views:

330

answers:

2

Hi, I am using base36 to shorten URLs. I have an id of a blog entry and convert that id to base36 to make it smaller. Base36 only includes lowercase letters. How can I include uppercase letters? If I use base64_encode it actually makes the string longer.

Thanks, Max

A: 
function dec2any( $num, $base=62, $index=false ) {

    // Parameters:
    //   $num - your decimal integer
    //   $base - base to which you wish to convert $num (leave it 0 if you are providing $index or omit if you're using default (62))
    //   $index - if you wish to use the default list of digits (0-1a-zA-Z), omit this option, otherwise provide a string (ex.: "zyxwvu")

    if (! $base ) {
        $base = strlen( $index );
    } else if (! $index ) {
        $index = substr( "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" ,0 ,$base );
    }
    $out = "";
    for ( $t = floor( log10( $num ) / log10( $base ) ); $t >= 0; $t-- ) {
        $a = floor( $num / pow( $base, $t ) );
        $out = $out . substr( $index, $a, 1 );
        $num = $num - ( $a * pow( $base, $t ) );
    }
    return $out;
}

Shamelessly borrowed from a commenter on PHP's base_convert() page (base_convert() only works up to base 32).

cpharmston
This is not random right? This basicly the same as base32 but with added uppercase?
mistero
since this function uses floating-point operations like log10 and pow. there is some chance that round-off error could seep in for a some input values.
Kip
input values are only numbers...?
mistero
but the numbers are integers, then you do floating-point math on them. it *could* be that this is ok for every possible input, but when i see people doing log10() a red flag goes off. it is pretty difficult to prove (short of brute force) that the algorithm will be ok for all inputs. in this case it's not necessary either--Pascal's answer does it only with integer math.
Kip
Agreed. I upvote Pascal's answer, but will leave this here for the good discussion.
cpharmston
+3  A: 

Hi,

you can find examples of source-code to create short-urls containing letters (both lower and upper case) and number on those two articles, for instance :

Here is the portion of code used in that second article (quoting) :

$codeset = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
$base = strlen($codeset);
$n = 300;
$converted = "";

while ($n > 0) {
  $converted = substr($codeset, ($n % $base), 1) . $converted;
  $n = floor($n/$base);
}

echo $converted; // 4Q

And you can pretty easily encapsulate this in a function -- only thing to consider is that $n is to be received as a parameter :

function shorten($n) {
    $codeset = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
    $base = strlen($codeset);
    $converted = "";
    while ($n > 0) {
      $converted = substr($codeset, ($n % $base), 1) . $converted;
      $n = floor($n/$base);
    }
    return $converted;
}

And calling it this way :

$id = 123456;
$url = shorten($id);
var_dump($url);

You get :

string 'w7e' (length=3)

(You can also add some other characters, if needed -- depending on what you want to get in your URLs)


Edit after the comment :

Reading through the second article (from which I got the shortening code), you'll find the code that does the un-shortening.

Encapsulating that code in a function shouldn't be that hard, and might get you something like this :

function unshorten($converted) {
    $codeset = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
    $base = strlen($codeset);
    $c = 0;
    for ($i = strlen($converted); $i; $i--) {
      $c += strpos($codeset, substr($converted, (-1 * ( $i - strlen($converted) )),1)) 
            * pow($base,$i-1);
    }
    return $c;
}

And calling it with a shortened-url :

$back_to_id = unshorten('w7e');
var_dump($back_to_id);

Will get you :

int 123456
Pascal MARTIN
How would I go ahead and decode it once encoded with this method?
mistero
@mistero : in the article I linked to, there is both the encoding and decoding code, which you can wrap into a function, like I did for the encoding one ;; Still, I've edited my answer to show what it might look like.
Pascal MARTIN