我正在编写一个url缩短函数,用于我正在学习php的项目,这里是代码(顺便说一下,我认为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不正确,它应该是aaa,但对于3位数来说太长了号。
此外,我开始认为这段代码有问题,如果我传递1'000'000'000'000 $id
我得到nItOq ...我觉得这是错误的,因为网址缩短像bit.ly这样的服务如果我使用它会返回一个6位数的id,我认为这个算法不比他们好。
所以,有两个问题:
答案 0 :(得分:11)
只需要几个小的调整,主要的两个是使字母表零索引而不是一个索引,并在分割之前从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;
}
编辑:排序的连接与OP相同
答案 1 :(得分:5)
如果你正在寻找相反的功能来获取base64号码并转换为base10,这里有一些PHP基于这个答案中的JavaScript: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;
}
答案 2 :(得分:1)
顺便说一下,看看base_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");
也许会有所帮助?
答案 3 :(得分:1)
Paul Greg创建了一些从Base-10转换为另一个基础的PHP代码。这可以在这里测试并下载代码:
http://www.pgregg.com/projects/php/base_conversion/base_conversion.php
我使用这种方法将数据库行ID转换为Base-64。缩短这些数字后,可以在网址中使用这些数字。 [details]
答案 4 :(得分:1)
这个怎么样:
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;
}
答案 5 :(得分:0)
这两个功能非常方便,这要感谢@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";
答案 6 :(得分:0)
这是Nathans代码的一种变体,用于处理大于PHP_INT_MAX的大整数。
这使用应该在Windows服务器上内置的BC Maths Functions,但是需要在Unix服务器上将其作为可选扩展启用。此解决方案还需要几个自定义BC函数来处理我从post by Alix Axel复制来的地板和圆形函数。
function shorten($value, $alphabet = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-') {
$base = strlen($alphabet);
$result = '';
while ($value) {
$mod = bcmod($value, $base);
$value = bcfloor(bcdiv($value, $base));
$result = $alphabet[$mod] . $result;
}
return $result;
}
function lengthen($value, $alphabet = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-') {
$base= strlen($alphabet);
$result = '';
for($i = 0, $limit = strlen($value); $i < $limit; $i++) {
$result = bcadd(bcmul($base, $result), strpos($alphabet, $value[$i]));
}
return $result;
}
function bcceil($number) {
if (strpos($number, '.') !== false) {
if (preg_match("~\.[0]+$~", $number)) return bcround($number, 0);
if ($number[0] != '-') return bcadd($number, 1, 0);
return bcsub($number, 0, 0);
}
return $number;
}
function bcfloor($number) {
if (strpos($number, '.') !== false) {
if (preg_match("~\.[0]+$~", $number)) return bcround($number, 0);
if ($number[0] != '-') return bcadd($number, 0, 0);
return bcsub($number, 1, 0);
}
return $number;
}
function bcround($number, $precision = 0) {
if (strpos($number, '.') !== false) {
if ($number[0] != '-') return bcadd($number, '0.' . str_repeat('0', $precision) . '5', $precision);
return bcsub($number, '0.' . str_repeat('0', $precision) . '5', $precision);
}
return $number;
}
在Windows(32位)上运行PHP 5.6的示例
foreach ([0, 1, 9, 10, 115617, bcsub(PHP_INT_MAX, 1), PHP_INT_MAX, bcadd(PHP_INT_MAX, 1234567890)] as $value) {
$short = shorten($value);
$reversed = lengthen($short);
print shorten($value) . " ($value)<br>";
if ("$value" !== $reversed) {
print 'ERROR REVERSING VALUE<br>';
}
}
输出
0 (0)
1 (1)
9 (9)
a (10)
sex (115617)
1----_ (2147483646)
1----- (2147483647)
39Bwbh (3382051537)
如果ID是公开的,请避免在字符串中使用元音(例如,将115617缩短为性)。这将是应该提供安全字词的基础54版本。
$alphabet = '0123456789bcdfghjklmnpqrstvwxyzBCDFGHJKLMNPQRSTVWXYZ_-';
答案 7 :(得分:-1)
您可以使用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
。 ;)