我想创建一个唯一的 id,但uniqid()
正在提供类似'492607b0ee414'
. 我想要的是类似于 tinyurl 给出的东西:'64k8ra'
。越短越好。唯一的要求是它不应该有一个明显的顺序,并且它应该看起来比看似随机的数字序列更漂亮。字母优于数字,理想情况下不会大小写混合。由于条目的数量不会那么多(最多 10000 个左右),因此碰撞的风险并不是一个很大的因素。
任何建议表示赞赏。
我想创建一个唯一的 id,但uniqid()
正在提供类似'492607b0ee414'
. 我想要的是类似于 tinyurl 给出的东西:'64k8ra'
。越短越好。唯一的要求是它不应该有一个明显的顺序,并且它应该看起来比看似随机的数字序列更漂亮。字母优于数字,理想情况下不会大小写混合。由于条目的数量不会那么多(最多 10000 个左右),因此碰撞的风险并不是一个很大的因素。
任何建议表示赞赏。
制作一个返回给定长度的随机字母的小函数:
<?php
function generate_random_letters($length) {
$random = '';
for ($i = 0; $i < $length; $i++) {
$random .= chr(rand(ord('a'), ord('z')));
}
return $random;
}
然后你会想要调用它,直到它是唯一的,在伪代码中取决于你存储该信息的位置:
do {
$unique = generate_random_letters(6);
} while (is_in_table($unique));
add_to_table($unique);
您可能还想确保这些字母不会在字典中构成单词。可以是整个英文词典,也可以只是一个坏词词典,以避免客户发现不好的东西。
编辑:我还要添加这个只有当你打算使用它时才有意义,因为它不是用于大量项目,因为你得到的冲突越多,这可能会变得非常慢(在表中获得一个 ID)。当然,您需要一个索引表,并且您需要调整 ID 中的字母数量以避免冲突。在这种情况下,使用 6 个字母,您将拥有 26^6 = 308915776 个可能的唯一 ID(减去坏词),这足以满足您 10000 个的需求。
编辑:如果您想要字母和数字的组合,您可以使用以下代码:
$random .= rand(0, 1) ? rand(0, 9) : chr(rand(ord('a'), ord('z')));
@ gen_uuid()来自gord。
preg_replace 有一些讨厌的 utf-8 问题,导致 uid 有时包含“+”或“/”。为了解决这个问题,您必须明确地将模式设为 utf-8
function gen_uuid($len=8) {
$hex = md5("yourSaltHere" . uniqid("", true));
$pack = pack('H*', $hex);
$tmp = base64_encode($pack);
$uid = preg_replace("#(*UTF8)[^A-Za-z0-9]#", "", $tmp);
$len = max(4, min(128, $len));
while (strlen($uid) < $len)
$uid .= gen_uuid(22);
return substr($uid, 0, $len);
}
我花了很长时间才找到它,也许它可以避免其他人头疼
你可以用更少的代码来实现:
function gen_uid($l=10){
return substr(str_shuffle("0123456789abcdefghijklmnopqrstuvwxyz"), 0, $l);
}
结果(示例):
有两种方法可以获得一个可靠的唯一 ID:让它变长且可变,以致发生冲突的机会非常小(如 GUID)或将所有生成的 ID 存储在一个表中以供查找(在内存或数据库中)或文件)在生成时验证唯一性。
如果您真的在问如何在没有某种重复检查的情况下生成这样一个短密钥并保证其唯一性,那么答案是,您不能。
这是我用于任意长度的随机 base62 的例程...
调用gen_uuid()
返回字符串等WJX0u0jV, E9EMaZ3P
。
默认情况下,这会返回 8 位数字,因此空间为 64^8 或大约 10^14,这通常足以使碰撞非常罕见。
对于更大或更小的字符串,根据需要传入 $len。长度没有限制,因为我会追加直到满足 [最多 128 个字符的安全限制,可以删除]。
请注意,在md5 [或 sha1,如果您愿意]中使用随机盐,因此它不容易被逆向工程。
我在网络上没有找到任何可靠的 base62 转换,因此这种从 base64 结果中剥离字符的方法。
在 BSD 许可下自由使用,享受,
function gen_uuid($len=8)
{
$hex = md5("your_random_salt_here_31415" . uniqid("", true));
$pack = pack('H*', $hex);
$uid = base64_encode($pack); // max 22 chars
$uid = ereg_replace("[^A-Za-z0-9]", "", $uid); // mixed case
//$uid = ereg_replace("[^A-Z0-9]", "", strtoupper($uid)); // uppercase only
if ($len<4)
$len=4;
if ($len>128)
$len=128; // prevent silliness, can remove
while (strlen($uid)<$len)
$uid = $uid . gen_uuid(22); // append until length achieved
return substr($uid, 0, $len);
}
非常简单的解决方案:
使用以下命令创建唯一 ID:
$id = 100;
base_convert($id, 10, 36);
再次获取原始值:
intval($str,36);
不能因为它来自另一个堆栈溢出页面而对此表示赞赏,但我认为该解决方案非常优雅和令人敬畏,值得将其复制到该线程以供参考。
如果您想来回转换它,您可以使用 Id 并将其转换为 base-36 数字。可用于任何具有整数 id 的表。
function toUId($baseId, $multiplier = 1) {
return base_convert($baseId * $multiplier, 10, 36);
}
function fromUId($uid, $multiplier = 1) {
return (int) base_convert($uid, 36, 10) / $multiplier;
}
echo toUId(10000, 11111);
1u5h0w
echo fromUId('1u5h0w', 11111);
10000
聪明的人可能可以通过足够多的 id 示例来解决这个问题。不要让这种默默无闻取代安全性。
我想出了一个我认为非常酷的解决方案,无需进行唯一性检查。我想我会分享给任何未来的访客。
计数器是保证唯一性的一种非常简单的方法,或者如果您使用数据库,主键也可以保证唯一性。问题是它看起来很糟糕并且可能很脆弱。所以我把这个序列和一个密码混在一起。由于密码可以反转,我知道每个 id 都是唯一的,同时仍然显得随机。
它是 python 而不是 php,但我在这里上传了代码: https ://github.com/adecker89/Tiny-Unique-Identifiers
字母很漂亮,数字很丑。您想要随机字符串,但不想要“丑陋”的随机字符串?
创建一个随机数并以alpha 样式( base-26 ) 打印它,就像航空公司提供的预订“数字”一样。
据我所知,PHP 中没有内置通用的基本转换函数,因此您需要自己编写代码。
另一种选择:使用uniqid()
和摆脱数字。
function strip_digits_from_string($string) {
return preg_replace('/[0-9]/', '', $string);
}
或者用字母替换它们:
function replace_digits_with_letters($string) {
return strtr($string, '0123456789', 'abcdefghij');
}
你也可以这样做:
public static function generateCode($length = 6)
{
$az = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$azr = rand(0, 51);
$azs = substr($az, $azr, 10);
$stamp = hash('sha256', time());
$mt = hash('sha256', mt_rand(5, 20));
$alpha = hash('sha256', $azs);
$hash = str_shuffle($stamp . $mt . $alpha);
$code = ucfirst(substr($hash, $azr, $length));
return $code;
}
你可以做到这一点,而不需要像循环、字符串连接或多次调用 rand() 这样的不干净/昂贵的东西,以一种干净且易于阅读的方式。此外,最好使用mt_rand()
:
function createRandomString($length)
{
$random = mt_rand(0, (1 << ($length << 2)) - 1);
return dechex($random);
}
如果您在任何情况下都需要字符串具有确切的长度,只需用零填充十六进制数:
function createRandomString($length)
{
$random = mt_rand(0, (1 << ($length << 2)) - 1);
$number = dechex($random);
return str_pad($number, $length, '0', STR_PAD_LEFT);
}
“理论上的倒退”是,您仅限于 PHP 的能力——但在这种情况下,这更像是一个哲学问题;)无论如何,让我们来看看:
$length <= 8
至少在 32 位系统上,PHP 对此的限制应该是 4.294.967.295 。mt_rand()
至少在32 位系统上,它应该是 2.147.483.647回到主题 - 直觉do { (generate ID) } while { (id is not uniqe) } (insert id)
有一个缺点和一个可能的缺陷,可能会让你直接陷入黑暗......
缺点:验证是悲观的。这样做总是需要检查数据库。拥有足够的密钥空间(例如 10k 条目的长度为 5)不太可能经常导致冲突,因为仅尝试存储数据并仅在 UNIQUE KEY 错误的情况下重试可能会消耗更少的资源。
缺陷: 用户 A检索到一个被验证为尚未被使用的 ID。然后代码将尝试插入数据。但与此同时,用户 B进入了相同的循环,不幸的是检索到了相同的随机数,因为用户 A尚未存储,并且此 ID 仍然空闲。现在系统存储User B或User A,当尝试存储第二个用户时,同时已经有另一个用户 - 具有相同的 ID。
在任何情况下,您都需要处理该异常,并且需要使用新创建的 ID 重新尝试插入。在保持悲观检查循环(您需要重新输入)的同时添加这个将导致非常丑陋且难以遵循的代码。幸运的是,解决这个问题的方法与解决问题的方法相同:一开始就去做,然后尝试存储数据。如果出现 UNIQUE KEY 错误,只需使用新 ID 重试即可。
看看这篇文章
它解释了如何从您的 bdd id 生成简短的唯一 id,就像 youtube 一样。
实际上,文章中的函数与php 函数 base_convert非常相关,它将一个数字从一个基数转换为另一个数(但最多只能达到 36 个基数)。
10 个字符:
substr(uniqid(),-10);
5个二进制字符:
hex2bin( substr(uniqid(),-10) );
8 个 base64 字符:
base64_encode( hex2bin( substr(uniqid(),-10) ) );
function rand_str($len = 12, $type = '111', $add = null) {
$rand = ($type[0] == '1' ? 'abcdefghijklmnpqrstuvwxyz' : '') .
($type[1] == '1' ? 'ABCDEFGHIJKLMNPQRSTUVWXYZ' : '') .
($type[2] == '1' ? '123456789' : '') .
(strlen($add) > 0 ? $add : '');
if(empty($rand)) $rand = sha1( uniqid(mt_rand(), true) . uniqid( uniqid(mt_rand(), true), true) );
return substr(str_shuffle( str_repeat($rand, 2) ), 0, $len);
}
如果您喜欢更长版本的唯一 ID,请使用:
$uniqueid = sha1(md5(time()));
迄今为止的最佳答案:给定唯一数据库 ID 的最小唯一“类似哈希”字符串 - PHP 解决方案,不需要第三方库。
这是代码:
<?php
/*
THE FOLLOWING CODE WILL PRINT:
A database_id value of 200 maps to 5K
A database_id value of 1 maps to 1
A database_id value of 1987645 maps to 16LOD
*/
$database_id = 200;
$base36value = dec2string($database_id, 36);
echo "A database_id value of 200 maps to $base36value\n";
$database_id = 1;
$base36value = dec2string($database_id, 36);
echo "A database_id value of 1 maps to $base36value\n";
$database_id = 1987645;
$base36value = dec2string($database_id, 36);
echo "A database_id value of 1987645 maps to $base36value\n";
// HERE'S THE FUNCTION THAT DOES THE HEAVY LIFTING...
function dec2string ($decimal, $base)
// convert a decimal number into a string using $base
{
//DebugBreak();
global $error;
$string = null;
$base = (int)$base;
if ($base < 2 | $base > 36 | $base == 10) {
echo 'BASE must be in the range 2-9 or 11-36';
exit;
} // if
// maximum character string is 36 characters
$charset = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
// strip off excess characters (anything beyond $base)
$charset = substr($charset, 0, $base);
if (!ereg('(^[0-9]{1,50}$)', trim($decimal))) {
$error['dec_input'] = 'Value must be a positive integer with < 50 digits';
return false;
} // if
do {
// get remainder after dividing by BASE
$remainder = bcmod($decimal, $base);
$char = substr($charset, $remainder, 1); // get CHAR from array
$string = "$char$string"; // prepend to output
//$decimal = ($decimal - $remainder) / $base;
$decimal = bcdiv(bcsub($decimal, $remainder), $base);
} while ($decimal > 0);
return $string;
}
?>