在PHP中生成唯一标识符(用于URL缩短器)

5

如何在PHP中使用(0-9),(a-z)和(A-Z)生成最多6个字符的唯一组合?有多少可能的组合?(例如AAaa与Aaaa不同)


2
uniqid() http://nz.php.net/manual/zh/function.uniqid.php - user557846
2
问题不在于生成它们,而在于确保它们保持独特。您打算如何存储它们? - Gordon
1
如果您生成一个ID,仍然需要每次检查以确保它之前没有被生成过;但如果您使用uniquid(),则无需每次检查。 - user557846
@adam,您能否提供有关您的场景的更多信息。这些ID用于什么?您可以在数据库中使用存储过程吗?ID是否可以“释放”,例如当它们在数据库中被删除时,它们应该被重新分配还是永久消耗?它们必须是a-Z0-9,还是一个持续不断的数字也可以工作? - Gordon
可能是重复的问题:如何编写URL缩短器?,更多PHP相关内容请参考:http://stackoverflow.com/search?q=php+url+shortener。 - Gordon
显示剩余4条评论
2个回答

6
使用base_convert($number, 10, 36)不能像问题所指定的那样将a-zA-Z区分开来。您需要编写自定义函数。
在您的数据库中使用int列作为主键,并在插入时自动递增该列,然后在永久链接的逻辑中将此ID从十进制转换为62进制(62允许使用0-9、a-z和A-Z)。
在创建新的永久链接时:
<?php

/**
 * Convert decimal int to a base-62 string
 *
 * @param int $dec
 * @returns string
 */
function toBase62 ($dec) {

  // 0 is always 0
  if ($dec == 0)
    return "0";

  // this array maps decimal keys to our base-62 radix digits
  $values = array(
    "0", "1", "2", "3", "4", 
    "5", "6", "7", "8", "9", 
    "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"
  );

  // convert negative numbers to positive.
  $neg = $dec < 0;
  if ($neg)
    $dec = 0 - $dec;

  // do the conversion:
  $chars = array(); // this will store our base-62 chars

  while ($dec > 0) {

    $val = $dec % 62;

    $chars[] = $values[$val];

    $dec -= $val;
    $dec /= 62;

  }

  // add zero-padding:
  while (count($chars) < 6)
    $chars[] = '0';

  // convert to string
  $rv = implode( '' , array_reverse($chars) );

  // if input was negative:
  return $neg ? "-$rv" : $rv;

}


// Usage example:

// ... do mysql insert here and retrieve new insert_id into var $id ...

$permalink = toBase62($id);

?>

在解码请求的永久链接时:

<?php

/**
 * Convert base-62 string to a decimal int
 *
 * @param string $str
 * @returns int on success, FALSE on failure
 */
function base62ToInt ($str) {

  // validate str:
  if ( ! preg_match('/^\-?[0-9A-Za-z]+$/', $str) )
    return FALSE; // not a valid string

  // 0 is always 0
  if ($str == "0" )
    return 0;

  // this array maps decimal keys to our base-62 radix digits
  $values = array(
    "0", "1", "2", "3", "4", 
    "5", "6", "7", "8", "9", 
    "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"
  );

  // flip $values so it maps base-62 digits to decimal values:
  $values = array_flip($values);

  // get chars from $str:
  $chars = str_split($str);

  // convert negative numbers to positive.
  $neg = $chars[0] == '-';

  if ($neg)
    array_shift($chars);

  // do the conversion:
  $val = 0;
  $i = 0;

  while ( count($chars) > 0 ) {

    $char = array_pop($chars);
    $val += ($values[$char] * pow(62, $i) );
    ++$i;

  }

  return $neg ? 0 - $val : $val;
}


// Usage example:

// ... assuming permalink has been put in a var called $permalink

$id = base62ToInt($permalink);

// ... now look up $id in DB

?>

这个算法很好,但结果的URL不够美观。对于第三行的结果000003,它似乎不是一个缩短的URL。你认为呢? - Rajib

4
我的建议是(特别是如果你已经使用数据库来存储它们),让数据库使用自动递增数字生成唯一的行 ID,然后将该数字转换为你的代码,这样可以确保它是唯一的,因为它是由数据库生成的。
关于从数字生成代码,我的建议是进行简单的替换,创建一个包含所有可能字符的字符串,并将你的数字转换为基数62(所有字符的计数)并为每个字符替换一个字母或数字。
AaBbCcDd...0123456789

顺便提一下,我建议删除lIioO01,因为它们非常相似。

如下代码所示,正如Dan Grossman在下面的评论中建议的那样,它将为您提供非常接近您想要的结果。

$code = base_convert($number, 10, 36);

这将基于数字和字母0-9和a-z为您提供一个编号。


1
"$code = base_convert($number, 10, 36);" 将使用0-9和a-z。这应该足够简短,以至于您不需要手动编写更高的基数。 - Dan Grossman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接