代码之家  ›  专栏  ›  技术社区  ›  Alberto Zaccagni

base10到base64 URL缩短

  •  5
  • Alberto Zaccagni  · 技术社区  · 14 年前

    我正在为一个学习PHP的项目编写一个URL缩短器函数,这里是代码(顺便说一句,我想 global 这不是一件好事:p):

    $alphabet = array(1 => "a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z",
                    "A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z",
                    "0","1","2","3","4","5","6","7","8","9","_","-");
    
    function shorten($id){
        global $alphabet;
        $shortenedId = "";
        while($id>0){
            $remainder = $id % 64;
            $id = $id / 64;     
            $shortenedId = $alphabet[$remainder].$shortenedId;
        }
        return $shortenedId;
    }
    

    代码取自 this Wikipedia article 并适应PHP。我的问题是,当我将64的倍数传递给函数时,得到了一个错误的结果(出于我的目的),例如128返回B,这是不正确的,它应该是a a a,但是对于一个3位数字来说太长了。

    而且,我开始认为这段代码有问题,如果我把1000'000'000'000作为 $id 我得到NITOQ…我觉得这是错误的,因为像bit.ly这样的URL缩短服务如果我使用它,会返回一个6号的ID,而且我不认为这个算法比他们的算法更好。

    所以,有两个问题:

    • 你在上面的代码中发现了任何错误吗?
    • 如何管理64个多ID?我是否必须忽略它们并传递给下一个?
    7 回复  |  直到 6 年前
        1
  •  11
  •   nathan    14 年前

    只需要做一些小的调整,主要的两个是使字母表变为零索引而不是一个索引,并在除法之前从ID中减去余数。

    function shorten($id)
    {
        $alphabet = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-';
        $shortenedId = '';
        while($id>0) {
            $remainder = $id % 64;
            $id = ($id-$remainder) / 64;     
            $shortenedId = $alphabet{$remainder} . $shortenedId;
        };
        return $shortenedId;
    }
    

    这里有一个更进一步的修改版本…我只是喜欢

    function shorten($id, $alphabet='0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-')
    {
        $base = strlen($alphabet);
        $short = '';
        while($id) {
            $id = ($id-($r=$id%$base))/$base;     
            $short = $alphabet{$r} . $short;
        };
        return $short;
    }
    

    编辑: 排序后的连接与操作相同

        2
  •  5
  •   Community CDub    8 年前

    如果您正在寻找相反的函数来获取base64数字并转换为base10,下面是一些基于此答案中的javascript的PHP: How to convert base64 to base10 in PHP?

    function lengthen($id) {
        $alphabet='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-';
    
        $number=0;
        foreach(str_split($id) as $letter) {
            $number=($number*64) + strpos($alphabet,$letter);
        }
        return $number;
    }
    
        3
  •  1
  •   Richard Knop    14 年前

    顺便说一下,检查一下基本的convert()函数( http://php.net/manual/en/function.base-convert.php ):

    echo base_convert(1000000000, 10, 36);
    

    36是它能转换成的最长的基数。但在评论部分,我发现:

    function dec2any( $num, $base, $index=false ) {
        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;
    }
    
    echo dec2any(1000000000, 64, "_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789");
    

    也许会有帮助?

        4
  •  1
  •   Gabe Sumner    13 年前

    PaulGreg创建了一些从Base-10转换到另一个Base的PHP代码。可以在这里测试和下载代码:

    http://www.pgregg.com/projects/php/base_conversion/base_conversion.php

    我使用这种方法将数据库行ID转换为base-64。一旦这些数字被缩短,就可以在URL中使用。 [details]

        5
  •  1
  •   malhal Benzy Neez    6 年前

    这个怎么样?

    function shorten_int($id){
        $hex = base_convert(id, 10, 16);
        $base64 = base64_encode(pack('H*', $hex));
        //$base64 = str_replace("/", "_", $base64); // remove unsafe url chars
        //$base64 = str_replace("+", "-", $base64);
        //$base64 = rtrim($base64, '='); // Remove the padding "=="
        $replacePairs = array('/' => '_',
                              '+' => '-',
                              '=' => '');
        $base64 = strtr($base64, $replacePairs); // optimisation
        return $base64;
    }
    
        6
  •  0
  •   diyism    6 年前

    由于@malhal,这两个功能非常方便:

    function shorten_int($id)
    {
        $id=dechex($id);
        $id=strlen($id)%2===0?hex2bin($id):hex2bin('0'.$id);
        $id=base64_encode($id);
        $id=strtr($id, array('/'=>'_', '+'=>'-', '='=>''));
        return $id;
    }
    
    function unshorten_int($id)
    {
        $id=strtr($id, array('-'=>'+', '_'=>'/'));
        $id=base64_decode($id);
        $id=bin2hex($id);
        return base_convert($id, 16, 10);
    }
    
    echo shorten_int(43121111)."\n";
    echo unshorten_int(shorten_int(43121111))."\n";
    
        7
  •  -1
  •   Inkeliz    7 年前

    你可以使用 pack .

    $int = 1129717211140920362;
    
    $byte = pack('J*', $int);    
    echo base64_encode($byte); //= D62P0WqzFCo=
    

    它会导致 D62P0WqzFCo= ,这是正确的,因为 $int 是一个Int64,使用64位。base64对每个字符使用6位,因此它们需要大约11个字符。

    解码使用:

    $base64 = 'D62P0WqzFCo=';
    
    $byte = base64_decode($base64);
    echo unpack('J*',  $byte)[1]; //= 1129717211140920362
    

    它会回来 1129717211140920362 . ;)


    这是根据答案 Stackoverflow in Portuguese .