Создать случайную строку из 5 символов


102

Я хочу создать точную строку из 5 случайных символов с минимальной вероятностью дублирования. Как лучше всего это сделать? Спасибо.


2
у вас есть набор конкретных персонажей или только 0-9a-zA-Z?
Яник Рочон

Этот код гольф-челлендж может быть найден: codegolf.stackexchange.com/questions/119226/…
Titus

Используя, Random::alphanumericString($length)вы можете получить строки с 0-9a-zA-Z, которые получены из криптографически безопасных случайных данных.
каркать

Ответы:


162
$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? Нужно ли мне использовать escape-последовательности для включения этих символов?
Май

1
Второй фрагмент даже не использует параметр $lenввода ... вы его забыли?
TechNyquist

77

Если forпетель не хватает, я люблю использовать вот что:

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

Интересное решение ... очень краткое, хотя мне интересно, быстрее это или медленнее, чем при использовании for. Хотя не беспокойтесь о тестировании :-)
Arc

@matthew str_shuffle будет постоянно создавать дубликаты
SCC

@ user2826057: str_shuffle('ab')даст abили, baно никогда aa. Добавление str_repeatпозволяет для этого. Но, тем не менее, решение, которое я дал, на самом деле не является серьезным ... хотя оно действительно работает.
Мэтью

2
Вероятность того, что это произойдет, составляет 1/60466176, если ГСЧ распределен равномерно.
Мэтью

1
Это идеально подходит для нашего случая создания кодов ваучера. Мы удалили запутанные символы, как l, i, 1, 0, O, и т.д. , и из 500 ваучеров не получили дубликатов.
Люк Казинс

25

Самый быстрый способ - использовать самые изменчивые символы функции uniqid .

Например:

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


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

EDIT: если у вас есть поддержка OpenSSL в PHP, вы можете использовать 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 05

В этом случае вам следует заранее определить длину и сохранить ее как минимум в переменной. Использование strlen()в цикле не требует дополнительных затрат, особенно если вы уже знаете, что набор символов тем временем не изменится.
Arc

Сомневаюсь, php оптимизирован, в любом случае это вариант :)
hamboy75

7

Я всегда использую для этого одну и ту же функцию, обычно для генерации паролей. Это удобно и полезно.

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 всегда меняется с единицы на 0. Не лучше ли вместо этого рандомизировать $ alt?
Нико Андраде


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

Если нормально, что вы получите только буквы AF, то вот мое решение:

str_pad(dechex(mt_rand(0, 0xFFFFF)), 5, '0', STR_PAD_LEFT);

Я считаю, что использование хеш-функций является излишним для такой простой задачи, как генерация последовательности случайных шестнадцатеричных цифр. dechex+ mt_randвыполнит ту же работу, но без лишней криптографической работы. str_padгарантирует длину выходной строки 5 символов (если случайное число меньше 0x10000).

Вероятность повторения зависит от mt_randнадежности России. Mersenne Twister известен качественной случайностью, поэтому он должен хорошо соответствовать задаче.


2

Я также не знал, как это сделать, пока не подумал об использовании 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"

1

Подобно ответу Брэда Кристи , но с использованием 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()до 1 миллиона раз более предсказуемо, чем microtime().
Arc

Также обратите внимание на мои комментарии к ответу Брэда, более серьезная проблема заключается в том, что сгенерированный хеш является шестнадцатеричным представлением, состоящим из 16 действительных символов, поэтому остается только 1024 варианта для $str.
Arc

@Archimedix, возможно, но OP не требовал безопасности, вопрос был определен, чтобы иметь алгоритм генерации строки, состоящий из комбинации 5x26 разных символов, и избегая дублирования. Вероятно, это для ссылочного номера подтверждения в процессе регистрации (или в процессе выставления счетов) или чего-то
подобного

Насколько я понимаю, он действительно просил о наименьшей возможности дублирования , и это имеет вероятность 0,1% (1 из 1024), что могло быть почти 1 из 1 миллиарда.
Arc

однако, если вам нужно сгенерировать ссылочный ключ из 5 символов и предоставить вам клиента / покупателя, вы не хотите давать им " ˫§»⁋⅓" просто потому, что это более безопасно ... вы увеличиваете ссылочный ключ до 7 или более символов . (Кстати: исправление в моем предыдущем комментарии, это 5x36 символов)
Яник Рочон,


1

Источник: функция PHP, которая генерирует случайные символы

У меня сработала эта простая функция PHP:

function cvf_ps_generate_random_code($length=10) {

   $string = '';
   // You can define your own characters here.
   $characters = "23456789ABCDEFHJKLMNPRTVWXYZabcdefghijklmnopqrstuvwxyz";

   for ($p = 0; $p < $length; $p++) {
       $string .= $characters[mt_rand(0, strlen($characters)-1)];
   }

   return $string;

}

Использование:

echo cvf_ps_generate_random_code(5);

1

Вот мои random 5 cents...

$random=function($a, $b) {
    return(
        substr(str_shuffle(('\\`)/|@'.
        password_hash(mt_rand(0,999999),
        PASSWORD_DEFAULT).'!*^&~(')),
        $a, $b)
    );
};

echo($random(0,5));

Новая password_hash()функция PHP (*> = PHP 5.5) выполняет работу по генерации прилично длинного набора символов и чисел в верхнем и нижнем регистре.

Два конкат. строки до и после password_hashв функции $ random подходят для изменения.

Параметры для $random()* ($ a, $ b) на самом деле являются substr()параметрами. :)

ПРИМЕЧАНИЕ: это не обязательно должна быть функция, это может быть и обычная переменная ... как один неприятный однострочный элемент, например:

$random=(substr(str_shuffle(('\\`)/|@'.password_hash(mt_rand(0,999999), PASSWORD_DEFAULT).'!*^&~(')), 0, 5));

echo($random);

1
function CaracteresAleatorios( $Tamanno, $Opciones) {
    $Opciones = empty($Opciones) ? array(0, 1, 2) : $Opciones;
    $Tamanno = empty($Tamanno) ? 16 : $Tamanno;
    $Caracteres=array("0123456789","abcdefghijklmnopqrstuvwxyz","ABCDEFGHIJKLMNOPQRSTUVWXYZ");
    $Caracteres= implode("",array_intersect_key($Caracteres, array_flip($Opciones)));
    $CantidadCaracteres=strlen($Caracteres)-1;
    $CaracteresAleatorios='';
    for ($k = 0; $k < $Tamanno; $k++) {
        $CaracteresAleatorios.=$Caracteres[rand(0, $CantidadCaracteres)];
    }
    return $CaracteresAleatorios;
}

Совет профессионала - включите объяснение того, как ваш ответ решает проблему OP и почему он может отличаться от других уже предоставленных ответов.
Tom

0

Я всегда использую это:

<?php function fRand($len) {
    $str = '';
    $a = "abcdefghijklmnopqrstuvwxyz0123456789";
    $b = str_split($a);
    for ($i=1; $i <= $len ; $i++) { 
        $str .= $b[rand(0,strlen($a)-1)];
    }
    return $str;
} ?>

Когда вы его вызываете, устанавливает длину строки.

<?php echo fRand([LENGHT]); ?>

Вы также можете изменить возможные символы в строке $a.


Я вижу, что теряю время, праздно разрабатывая эту функцию. Я немного погуглил! @ Андрей
LipESprY
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.