生成随机的5个字符字符串

111

我想要创建一个由5个随机字符组成的字符串,最好的方式是减少出现重复的可能性。请问应该怎么做?谢谢。


2
你有一组特定的字符吗,还是只有 0-9a-zA-Z - Yanick Rochon
这个代码高尔夫挑战可能是一个好发现:https://codegolf.stackexchange.com/questions/119226/make-me-a-password-generator - Titus
使用Random::alphanumericString($length),您可以获取由加密安全随机数据提供的0-9a-zA-Z字符串。 - caw
17个回答

177
$rand = substr(md5(microtime()),rand(0,26),5);

我认为这可能是最好的答案 - 除非您还在寻找特殊字符:

$seed = str_split('abcdefghijklmnopqrstuvwxyz'
                 .'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
                 .'0123456789!@#$%^&*()'); // and any other characters
shuffle($seed); // probably optional since array_is randomized; this may be redundant
$rand = '';
foreach (array_rand($seed, 5) as $k) $rand .= $seed[$k];

示例

而基于时钟的哈希函数(由于是增量式的,所以碰撞较少):

function incrementalHash($len = 5){
  $charset = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
  $base = strlen($charset);
  $result = '';

  $now = explode(' ', microtime())[1];
  while ($now >= $base){
    $i = $now % $base;
    $result = $charset[$i] . $result;
    $now /= $base;
  }
  return substr($result, -5);
}

注意:增量意味着更容易被猜测;如果你将其用作盐或验证令牌,请勿使用。现在使用的盐值"WCWyb",5秒后变成"WCWyg"


6
然而使用md5()的问题在于,你会得到一个由16个字符组成的字符串(10个数字和a到f,即每个字符4位)。这对于某些目的来说可能足够了,但对于加密目的来说可能太少了(16个符号中的五个字符= 16 ^ 5 = 20位= 1048576个可能性)。 - Arc
3
array_rand($seed, 5) 返回的是数组键名而非值,因此你的代码可能无法正常运行。 - alumi
1
我认为使用加密哈希函数生成随机字符至少是不合适的。 - gronostaj
$charset 中包含双引号 ", 单引号 ' 和反斜杠 \ 是可能的吗?我需要使用转义序列来包含这些字符吗? - Mai
1
第二个片段甚至没有使用$len输入参数...你忘记它了吗? - Niki Romagnoli
@TechNyquist 嘿,糟糕。我想这是复制粘贴错误。长度无关紧要,因为它是时间值的哈希(长度将根据时钟确定),不过如果太短,你可以左填充该值。在返回中也可以使用 -$len - Brad Christie

97

如果缺少for循环,这是我喜欢使用的方法:

$s = substr(str_shuffle(str_repeat("0123456789abcdefghijklmnopqrstuvwxyz", 5)), 0, 5);

@matthew str_shuffle会连续产生重复的字符。 - SCC
@user2826057:str_shuffle('ab')会给出abba,但永远不会是aa。添加str_repeat可以实现这一点。但话说回来,我提供的解决方案并不是一个真正严肃的方案...虽然它确实有效。 - Matthew
@Matthew 当第二次运行此脚本时,它生成与第一次相同的代码。 - SCC
2
假设随机数生成器是均匀分布的,那么发生这种情况的概率为1/60466176。 - Matthew
1
这非常适合我们生成优惠券代码的用例。我们删除了混淆字符,如 li10O 等等,在 500 张优惠券中没有重复的。 - Luke Cousins
显示剩余4条评论

37
您可以简单地尝试如下所示:

您可以简单地尝试如下所示:

$length = 5;

$randomletter = substr(str_shuffle("abcdefghijklmnopqrstuvwxyz"), 0, $length);

更多细节请参见:http://forum.arnlweb.com/viewtopic.php?f=7&t=25

2
可以稍微解释一下 substr(str_shuffle(implode(range('a', 'z'))), 0, $length) - Ng Sek Long
2
这很好,没有花哨的东西,直截了当和简单。 [+1] - kapitan

31

一个快速的方法是使用 uniqid 函数中最不稳定的字符。

例如:

$rand = substr(uniqid('', true), -5);

-5 是指长度吗? - gumuruh
1
@gumuruh 这个问题是“…创建一个包含5个随机字符的字符串…”,所以将-5作为substr函数的偏移参数传递,将使用字符串中的最后五个字符。根据PHP文档的描述:“如果偏移量为负数,则返回的字符串将从字符串末尾的第offset个字符开始。” - John Parker

15

以下方法应该提供最小的重复机会(你可能想用更好的随机数源替换mt_rand(),例如来自/dev/*random或GUID):

<?php
    $characters = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
    $result = '';
    for ($i = 0; $i < 5; $i++)
        $result .= $characters[mt_rand(0, 61)];
?>

编辑:
如果您关心安全问题,请不要使用rand()mt_rand(),并验证您的随机数据设备确实是生成随机数据的设备,而不是类似于/dev/zero的常规文件或可预测的东西。 mt_rand()是有害的:
https://spideroak.com/blog/20121205114003-exploit-information-leaks-in-random-numbers-from-python-ruby-and-php

编辑: 如果PHP支持OpenSSL,则可以使用openssl_random_pseudo_bytes()函数:

<?php
    $length = 5;
    $randomBytes = openssl_random_pseudo_bytes($length);
    $characters = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
    $charactersLength = strlen($characters);
    $result = '';
    for ($i = 0; $i < $length; $i++)
        $result .= $characters[ord($randomBytes[$i]) % $charactersLength];
?>

第一个例子更聪明,使用 $result .= $characters[mt_rand(0, strlen($characters)-1)]。 - Hamboy75
在这种情况下,你应该预先确定长度并将其存储在一个变量中。在循环中使用 strlen() 是不必要的开销,特别是如果你已经知道字符集在此期间不会发生改变。 - Arc
我有所怀疑,PHP已经被优化了,不过这仍然是一个选择 :) - Hamboy75

8

我通常使用同一个函数来生成密码,这个函数非常容易使用且有用。

function randPass($length, $strength=8) {
    $vowels = 'aeuy';
    $consonants = 'bdghjmnpqrstvz';
    if ($strength >= 1) {
        $consonants .= 'BDGHJLMNPQRSTVWXZ';
    }
    if ($strength >= 2) {
        $vowels .= "AEUY";
    }
    if ($strength >= 4) {
        $consonants .= '23456789';
    }
    if ($strength >= 8) {
        $consonants .= '@#$%';
    }

    $password = '';
    $alt = time() % 2;
    for ($i = 0; $i < $length; $i++) {
        if ($alt == 1) {
            $password .= $consonants[(rand() % strlen($consonants))];
            $alt = 0;
        } else {
            $password .= $vowels[(rand() % strlen($vowels))];
            $alt = 1;
        }
    }
    return $password;
}

不错的代码。然而$alt总是从1翻转到0。随机化$alt会更好吧? - Nico Andrade

8

看起来str_shuffle可以很好地应用于此。 使用任何您想要的字符来种子洗牌。

$my_rand_strng = substr(str_shuffle("ABCDEFGHIJKLMNOPQRSTUVWXYZ"), -5);

http://php.net/manual/zh/function.substr.php http://php.net/manual/zh/function.str-shuffle.php - user6749930

5

在我想到使用PHP array之前,我也不知道如何做到这一点。而且我非常确定,这是使用数组生成随机字符串或数字的最简单方法。代码如下:

function randstr ($len=10, $abc="aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ0123456789") {
    $letters = str_split($abc);
    $str = "";
    for ($i=0; $i<=$len; $i++) {
        $str .= $letters[rand(0, count($letters)-1)];
    };
    return $str;
};

您可以如此使用此功能。
randstr(20)     // returns a random 20 letter string
                // Or like this
randstr(5, abc) // returns a random 5 letter string using the letters "abc"

3
$str = '';
$str_len = 8;
for($i = 0, $i < $str_len; $i++){
    //97 is ascii code for 'a' and 122 is ascii code for z
    $str .= chr(rand(97, 122));
}
return $str

2
Brad Christie的答案类似,但是使用sha1算法对字符0-9a-zA-Z进行加密,并在前面加上一个随机值:
$str = substr(sha1(mt_rand() . microtime()), mt_rand(0,35), 5);

但是,如果您设置了定义的(允许的)字符:
$validChars = array('0','1','2' /*...*/,'?','-','_','a','b','c' /*...*/);
$validCharsCount = count($validChars);

$str = '';
for ($i=0; $i<5; $i++) {
    $str .= $validChars[rand(0,$validCharsCount - 1)];
}

**更新**

正如Archimedix所指出的那样,这并不能保证返回“最小重复可能性”,因为给定字符范围的组合数很少。您可以增加字符数量,或在字符串中允许额外的(特殊)字符。我认为,在您的情况下,第一种解决方案更可取。


使用 time()microtime() 更可预测,高达100万倍。 - Arc
还要注意我的对Brad答案的评论,更大的问题是生成的哈希是由16个有效字符组成的十六进制表示,因此只留下了1024个变量供$str使用。 - Arc
@Archimedix,也许是这样,但是原帖没有要求安全性,问题被定义为一个由5x26不同字符组合而成的字符串生成算法,并且避免重复。这很可能是用于注册流程(或账单流程)中的确认参考号码,或其他类似情况。 - Yanick Rochon
据我理解,他确实要求最小化重复的可能性,这个概率是0.1%(1024分之1),几乎可以达到10亿分之1。 - Arc
然而,如果您需要生成一个5个字符的参考密钥并将其提供给客户,您不希望仅仅因为它更安全而将"˫§»⁋⅓"交给他们...您可以将参考密钥增加到7个或更多字符。(顺便说一句:在我之前的评论中有一个更正,它是5x36个字符) - Yanick Rochon
不过他说0-9,a-z和A-Z是有效字符,因此可以组成62个符号的集合,即62^5种可能性。哦,对不起,我的计算方法完全错误了...使用十六进制数字时不是1024种可能性,而是16^5=1048576。 - Arc

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